Édipo Féderle
Édipo Féderle

Reputation: 4257

Sort nested map Clojure

I am trying to sort a map by a key value. Given this map:

{:1 {:bar "something" :rank 10} :2 {:bar "other" :rank 20}}

I like to sort it by rank value:

{:2 {:bar "other" :rank 20} :1 {:bar "something" :rank 10} }

Its possible using sort-by ?

Thanks in advance

Upvotes: 3

Views: 1515

Answers (4)

Daniel Compton
Daniel Compton

Reputation: 14549

The other answers will work, but if you are going to be doing this command a lot, then you would be better off storing the data in a data.priority-map. It is designed for situations just like this.

A priority map is very similar to a sorted map, but whereas a sorted map produces a sequence of the entries sorted by key, a priority map produces the entries sorted by value.

; You'll need to add [org.clojure/data.priority-map "0.0.7"] to :dependencies first

user=> (require '[clojure.data.priority-map :as pm])
; nil
user=> (pm/priority-map-keyfn-by :rank > 
         :1 {:bar "something" :rank 10}
         :2 {:bar "other" :rank 20})
; {:2 {:rank 20, :bar "other"}, :1 {:rank 10, :bar "something"}}

Upvotes: 4

Mark Fisher
Mark Fisher

Reputation: 9886

See also this answer

Ascending:

user=> (sort-by (comp :rank second) < {:1 {:bar "something" :rank 10} :2 {:bar "other" :rank 20} :3 {:bar "bar" :rank 15}})
([:1 {:bar "something", :rank 10}] [:3 {:bar "bar", :rank 15}] [:2 {:bar "other", :rank 20}])

Descending:

user=> (sort-by (comp :rank second) > {:1 {:bar "something" :rank 10} :2 {:bar "other" :rank 20} :3 {:bar "bar" :rank 15}})
([:2 {:bar "other", :rank 20}] [:3 {:bar "bar", :rank 15}] [:1 {:bar "something", :rank 10}])

These both return the sequence of elements of map, to convert back to a map again, wrap it in (into {} ...)

Upvotes: 2

birdspider
birdspider

Reputation: 3074

sort-by takes a key function as well as an optional custom comparator

;              keyfn         comparator
(sort-by (comp :rank second) > 
  {:1 {:bar "something" :rank 10} :2 {:bar "other" :rank 20}})
;=> ([:2 {:bar "other", :rank 20}] [:1 {:bar "something", :rank 10}])

Upvotes: 3

Makoto
Makoto

Reputation: 106400

It's simple enough to do with sort-by, but since that sorts in ascending order, you'll have to use reverse to get it to go the right way.

(reverse
    (sort-by (comp :rank second)
        {:1 {:bar "something" :rank 10} :2 {:bar "other" :rank 20}}))

Upvotes: 3

Related Questions