关于排序:Clojure值上的排序图

Clojure sort map over value

我正在尝试根据值对地图进行排序。

输入图如下:

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

输出应如下所示:

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

我在这里的文档中使用了sorted-map-by:
http://clojuredocs.org/clojure.core/sorted-map-by

1
2
3
4
5
6
7
8
(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))
  )

我发现只有当关键字中没有特殊字符(如" /"或"-")时,此解决方案才有效。这是一个已知的错误吗?

那么如何在不编写自己的缓慢排序算法的情况下快速按值对映射进行排序?


这是基于@ user100464的建议的解决方案,当值相同时,应明确考虑键的比较。

注意:我选择通过将参数的顺序颠倒到比较中来减少排序:(compare (x k2)(x k1))和(compare k2 k1)

1
2
3
4
5
6
(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))

可以在(compare k2 k1)进行自定义,以实现更详细的键比较。


在我的Clojure 1.6.0 REPL中,问题中的代码已按值排序:

1
2
3
4
5
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}

如果要按键对具有相同值的条目进行排序,则需要对键进行字符串化。原因如下:

1
2
3
4
5
6
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")