2011-03-25 45 views
6

我有一個有序圖如下所示:如何在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]) 

回答

9

我排序的載體可能轉換爲哈希表來快速查找訂貨指數,導致這樣的事情:

{ :c 0 :a 1 } 

有幾個自動從seq/vector(例如maprange,然後reduce到{}與assoc)執行該操作的方法。將該結果(或上面的文字圖)綁定到本地(使用let),我們將其稱爲order-map

然後原始地圖(米)的條目進行過濾,僅包括包含在排序的那些:

(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} 
+0

我試過了,但我沒有定義「訂單地圖」。你嘗試過哪種版本的clojure? – Zubair 2011-03-25 09:37:04

+0

很抱歉,如果不清楚 - 您需要將生成的訂購地圖綁定到「訂單地圖」之類的東西。稍微更新瞭解釋。你需要做一些像 '(let [order-map {:c 0:a 1}]' ,然後在let-block裏面做其餘的部分。 – pmdj 2011-03-25 10:10:46

+0

啊,謝謝。 50個條目? – Zubair 2011-03-25 17:33:43

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),最終依靠底層數據結構的隱藏實現細節。接受的答案是正確的。

+0

輝煌,第一次工作! – Zubair 2011-03-25 09:58:30

+8

不幸的是,只要地圖變得足夠大,可以變成一個合適的哈希表,這將會失敗。嘗試一個有50個條目的例子,你會明白我的意思。 – pmdj 2011-03-25 10:27:41

+3

具體示例:'(asort(apply sorted-map(interleave(range 0 50)(range 0 50)))(range 32 0 -1))'should generate'{32 32 31 31 ... 1 1}''但在我的機器上,根本不是這種情況。 – pmdj 2011-03-25 10:34:50