4

I'm trying to sort a map over the values.

The input-map looks like:

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

The output should look like:

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

I used sorted-map-by like in the documentation here: 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 works only if the keywords have no special characters like "/" or "-" inside. Is this a known bug?

So how can I sort a map by values quickly without writing a own and slowly sort-algorithm?

0

2 Answers 2

2

In my Clojure 1.6.0 REPL, the code in the question already sorts 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 entries with the same value to be sorted by key, you need to stringify the keys. Here'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")
Sign up to request clarification or add additional context in comments.

3 Comments

I want to sort over the values in first order than maybe over the keys.
I'm not sure I understand your goal. Your code already produces a map that sorts by value. If you don't want to sort over the keys, you already have working code. If you do want to sort over the keys to disambiguate entries with the same value, you probably want to stringify the keys first.
In my REPL it does not sort the map over the values. The output and input is the same
2

Here is a solution based on the suggestion by @user100464 with explicit considerations of comparison of keys, when the values are the same.

Note: I choose to sort decreasingly by reversing the order of the arguments to the 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))

One may customize at (compare k2 k1) to implement more elaborate key comparison.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.