Reputation: 3308
I have a nested map which is structured like this (Clojurescript):
{"6841"
{"primaryTitle" "First name",
"secondaryTitle" "last name"},
"7944"
{"primaryTitle" "Test 2 first name",
"secondaryTitle" "Test 2 last name"}}
I then proceed to sort the map with the keys inside the nested map, like this:
(defn compare-title [x y]
(compare [(get (second x) "primaryTitle") (get (second x) "secondaryTitle")]
[(get (second y) "primaryTitle") (get (second y) "secondaryTitle")]))
(sort compare-title @loaded-assets)
So far the sorting works fine, but since sort function return the data structure like this:
["6841"
{"primaryTitle" "First name",
"secondaryTitle" "last name"}],
["7944"
{"primaryTitle" "Test 2 first name",
"secondaryTitle" "Test 2 last name"}]}
I have to use into {}
to transform the map back to the initial structure:
(into {} (sort compare-title my-map))
But this completely reverses the sorting which is made by sort. I've tried to replace into {}
with:
flatten
(which transforms this into list)apply hash-map
(which behaves similar to into {}
) reduce hash-map
(which preserves the order, but deeply nests each map into each other)So, is it possible to sort the map while preserving the structure?
Or how to transform back to the above original structure while preserving the sorted structure returned by sort
?
Upvotes: 1
Views: 483
Reputation: 1976
You can use priority-map
(use 'clojure.data.priority-map)
(defn compare-title [x y]
(compare [(get x "primaryTitle") (get x "secondaryTitle")]
[(get y "primaryTitle") (get y "secondaryTitle")]))
(apply priority-map-by compare-title
["7944"
{"primaryTitle" "Test 2 first name"
"secondaryTitle" "Test 2 last name"}
"6841"
{"primaryTitle" "First name"
"secondaryTitle" "last name"}])
;; => {"6841" {"primaryTitle" "First name", "secondaryTitle" "last name"}, "7944" {"primaryTitle" "Test 2 first name", "secondaryTitle" "Test 2 last name"}}
Upvotes: 6
Reputation: 29958
As already mentioned, maps cannot be sorted by values. They can be sorted by keys. Here is the easiest way with some helper functions:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(def data
{"6841"
{"primaryTitle" "First name",
"secondaryTitle" "last name"},
"7944"
{"primaryTitle" "Test 2 first name",
"secondaryTitle" "Test 2 last name"}})
(def data-sorted ; maybe use `postwalk` instead
(->sorted-map
(map-vals data ->sorted-map)))
(dotest
(is= data-sorted
{"6841" {"primaryTitle" "First name",
"secondaryTitle" "last name"},
"7944" {"primaryTitle" "Test 2 first name",
"secondaryTitle" "Test 2 last name"}}))
If you want to sort by the primary/secondary title, put those 2 items in a "sort key", then sort based on that:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(def all-data {"7944" {"primaryTitle" "Test 2 first name"
"secondaryTitle" "Test 2 last name"}
"6841" {"primaryTitle" "First name"
"secondaryTitle" "last name"}})
(dotest
(let [data-keyed (forv [entry all-data]
(let [[id title-map] entry]
{:sort-key [(grab "primaryTitle" title-map)
(grab "secondaryTitle" title-map)]
:id id}))
data-sorted (vec (sort-by :sort-key data-keyed))]
(is= (spy-pretty data-keyed)
[{:sort-key ["Test 2 first name" "Test 2 last name"] :id "7944"}
{:sort-key ["First name" "last name"] :id "6841"}])
(is= (spy-pretty data-sorted)
[{:sort-key ["First name" "last name"] :id "6841"}
{:sort-key ["Test 2 first name" "Test 2 last name"] :id "7944"}])))
Upvotes: 1