如何在Clojure中重新排序地图?
我有一张这样的有序地图:如何在Clojure中重新排序地图?,clojure,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] ) 我可能会将排序向量转换为哈希映射,以快速查找排序索引,结果如下: { :c 0 :a 1 } 有几种方法可以
{: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] )
我可能会将排序向量转换为哈希映射,以快速查找排序索引,结果如下:
{ :c 0 :a 1 }
有几种方法可以从一个seq/向量中自动执行该操作(例如,使用范围映射,然后使用关联将减少为{})。将结果(或上面的文字映射)绑定到本地(使用let
),我们称之为顺序映射
然后过滤原始映射(m)的条目,使其仅包括排序中包含的条目:
(select-keys m order)
并使用比较器函数(如下所示)将过滤后的表达式的结果放回新的排序映射(使用排序映射依据
):
(fn [a b] (compare (order-map a) (order-map b)))
请注意,如果您实际上不需要它作为一个映射,而序列也可以,那么您可以使用排序依据
和使用相同顺序映射的键函数
把这些放在一起,你会得到:
(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))))
以及:
下面是一个简单的方法:
(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}
更新:如评论中所述,此解决方案仅适用于小地图(请参阅),最终依赖于底层数据结构的隐藏实现细节。公认的答案是正确的。我试过了,但没有定义“顺序图”。您试用了哪个版本的clojure?如果不清楚,很抱歉-您需要将生成的排序映射绑定到类似于排序映射
的东西。更新了一点解释。您需要执行类似于(让[ordermap{:c0:a1}]
然后在let-block中执行其余操作。啊,谢谢。当有超过50个条目时,这个版本可以工作吗?是的,据我所知,这个版本在所有情况下都是正确的。据我所知,映射成为哈希表的截止点目前是32个条目,因此就是这个例子。事实上,(连词{}(选择键…)
似乎根本不起作用的是一个实现细节,在Clojure的未来版本中可能会发生变化。据我所知,映射基本上是列表,使用线性搜索直到32个条目的截止点,这就是它们保持排序的原因。除此之外,它们被实现为哈希树。@pmjordan截止点实际上是每年10个条目现在是这样。由于整数作为映射键散列的方式,它看起来可能是32,但如果使用小整数以外的其他值,很明显变化发生在10。不幸的是,一旦映射变得足够大,可以转换为适当的散列表,这将失败。尝试一个包含50个条目的示例,您将看到我所说的内容一个具体的例子:(asort(apply sorted map(interleave(range 0 50)(range 0 50)))(range 32 0-1))
应该生成{32 31 31…1}
,但在我的机器上根本不是这样。
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}