如何在Clojure中重新排序地图?

时间:2011-03-25 09:21:49

标签: clojure

我有一个有序的地图:

{:a 1 :b 2 :c 3}

:并给出了如下的排序列表:

[:c :a]

:我想找到最简单的方法:

{c: 3 :a 1}

:有谁知道怎么做?

更新

(defn asort [amap order]  (conj {} (select-keys amap order)))

(asort {:a 1 :b 2 :c 3} [:c :a] )

2 个答案:

答案 0 :(得分:9)

我可能会将排序向量转换为哈希映射以快速查找排序索引,从而产生如下结果:

{ :c 0  :a 1 }

有几种方法可以从seq / vector自动执行此操作(例如map range,然后reduce加{} assoc。将结果(或上面的文字地图)绑定到本地(使用let),我们称之为order-map

然后过滤原始地图(m)的条目,仅包括订购中包含的条目:

(select-keys m order)

使用比较器函数将过滤后的表达式的结果放回到新的有序映射中(使用sorted-map-by):

(fn [a b] (compare (order-map a) (order-map b)))

请注意,如果您实际上并不需要它作为地图,并且序列可以,则可以将sort-by与使用相同顺序图的键函数一起使用。

把这些放在一起,你得到:

(defn asort [m order]
  (let [order-map (apply hash-map (interleave order (range)))]
    (conj
      (sorted-map-by #(compare (order-map %1) (order-map %2))) ; empty map with the desired ordering
      (select-keys m order))))

=> (asort (apply sorted-map (interleave (range 0 50) (range 0 50))) (range 32 0 -1))
{32 32, 31 31, 30 30, 29 29, 28 28, 27 27, 26 26, 25 25, 24 24, 23 23, 22 22, 21 21, 20 20, 19 19, 18 18, 17 17, 16 16, 15 15, 14 14, 13 13, 12 12, 11 11, 10 10, 9 9, 8 8, 7 7, 6 6, 5 5, 4 4, 3 3, 2 2, 1 1}

答案 1 :(得分:1)

这是一种简单的方法:

(defn asort [amap order]
 (conj {} (select-keys amap order)))

导致:

clojure.core> (asort {:a 1 :b 2 :c 3} [:c :a])
{:c 3, :a 1}
clojure.core> (asort {:a 1 :b 2 :c 3} [:a :c])
{:a 1, :c 3}

更新:正如评论中所写,此解决方案仅适用于小地图(请参阅HASHTABLE_TRESHOLD),最终依赖于底层数据结构的隐藏实现细节。接受的答案是正确的。