Clojure sort map by value

I am trying to sort a map over values.

The input map looks like this:

{:Blabla 1, :foo 1, :bla-bla 1, :Bla 2, :bla/bla 1, :bla 4, :blub 2, :hello 1, :Foo 2}

      

The result should look like this:

{:bla 4 :Bla 2 :blub 2 :Foo 2 :Blabla 1 :bla-bla 1 :bla/bla 1 :foo 1 :hello 1}

      

I used a sorted map as in the documentation: http://clojuredocs.org/clojure.core/sorted-map-by

(defn sort-keyword-list [texts]
  (let [results (word-counts texts)]
       ;results is now {:Blabla 1, :foo 1, :bla-bla 1, :Bla 2, :bla/bla 1, :bla 4, :blub 2, :hello 1, :Foo 2}
       (into (sorted-map-by (fn [key1 key2]
                                (compare [(get results key2) key2]
                                         [(get results key1) key1])))
             results))
  )

      

Well, I found out that this solution only works if there are no special characters like "/" or "-" in the keywords. Is this a known bug?

So how can I sort a map by values ​​quickly without building my own slow sort algorithm?

+3


source to share


2 answers


In my Clojure 1.6.0 REPL, the code in the question is already sorted by value:

user=> (into (sorted-map-by (fn [key1 key2]
                     (compare [(get x key2) key2]
                              [(get x key1) key1])))
    x)
{:bla 4, :blub 2, :Foo 2, :Bla 2, :bla/bla 1, :hello 1, :foo 1, :bla-bla 1, :Blabla 1}

      



If you want records with the same value to be sorted by key, you need to compress the keys. That's why:

user=> x
{:bla-bla 1, :Blabla 1, :bla/bla 1, :hello 1, :bla 4, :foo 1, :Bla 2, :Foo 2, :blub 2}
user=> (sort (keys x))
(:Bla :Blabla :Foo :bla :bla-bla :blub :foo :hello :bla/bla)
user=> (sort (map str (keys x)))
(":Bla" ":Blabla" ":Foo" ":bla" ":bla-bla" ":bla/bla" ":blub" ":foo" ":hello")

      

+1


source


Here is a solution based on @ user100464's suggestion with an explicit consideration of key comparison when the values ​​are the same.

Note. I choose to sort in descending order, changing the order of the arguments to comparisons: (compare (x k2)

(x k1)) and (compare k2 k1)

.



(defn sort-by-value-then-key [x]
  (into (sorted-map-by (fn [k1, k2]
                         (let [v_c (compare (x k2) (x k1))]
                           (if (= 0 v_c)
                             (compare k2 k1)))))
        x))

      

Can be set to (compare k2 k1)

to implement more intelligent key comparison.

+2


source







All Articles