2014-10-29 86 views
2

我有一個clojure函數,它返回一個1鍵映射的序列。我想將這些地圖合併成一張地圖;然而,如果有相同的密鑰的地圖,我不想覆蓋這些值,只能將它們組合成一個向量。 merge似乎被覆蓋,而merge-with似乎嚴重扭曲了這種類型。合併映射而不覆蓋鍵

我:

({:foo "hello"} 
{:bar "world"} 
{:baz "!!!"} 
{:ball {:a "abc", :b "123"}} 
{:ball {:a "def", :b "456"}} 
{:ball {:a "ghi", :b "789"}}) 

我想:

{:foo "hello" 
:bar "world" 
:baz "!!!" 
:ball [{:a "abc", :b "123"} {:a "def", :b "456"} {:a "ghi", :b "789"}]} 

感謝。

+2

這是可能的,但我會退後一步,問爲什麼?根據是否有重複,有一個地圖的值可以是標量或矢量的用途是什麼?具有一致的值類型的地圖將更容易處理。 – Alex 2014-10-29 19:44:10

+0

我正在處理一個java後端。它的類型不是我的控制範圍。 [Clojure食譜]的 – 2014-10-29 19:57:58

+0

[本節](https://github.com/clojure-cookbook/clojure-cookbook/blob/master/02_composite-data/2-22_multiple-values/2-22_multiple-values.asciidoc) (https://duckduckgo.com/l/?kh=-1&uddg=http%3A%2F%2Fclojure-cookbook.com%2F)處理在同一張地圖上有單個和多個值。 – Thumbnail 2014-10-30 10:11:10

回答

1
(def data ...) ;; your list of maps 

(apply merge-with (comp flatten vector) data) 
;; => {:baz "!!!", :ball ({:b "123", :a "abc"} {:b "456", :a "def"} {:b "789", :a "ghi"}), :bar "world", :foo "hello"} 

注:在OP的情況下使用flatten的作品,但不是同時創造屬於碰撞鍵的值的向量合併地圖的通用方法。

+2

更好的希望沒有一個值被合併是向量。 – Alex 2014-10-29 19:44:30

+1

是啊...扁平化幾乎保證不做你想做的事情:/ – arrdem 2014-10-29 20:25:05

+0

@arrdem如果OP不知道什麼數據被返回,那麼是的,扁平化不是要走的路。你們中的任何一個人是否可以添加更適合一般情況的答案? – Kyle 2014-10-29 20:31:45

1

的「矢量安全」變種我能想出有遍歷所有鍵 - 值對兩次:

(->> (for [[k vs] (group-by key (apply concat data))] 
     (if (next vs) 
     [k (mapv val vs)] 
     (first vs))) 
    (into {})) 
;; => {:foo "hello", 
;;  :bar "world", 
;;  :baz "!!!", 
;;  :ball [{:a "abc", :b "123"} ...]} 

基本上,這組由鍵的所有值,只刪除它們周圍的序列如果它只包含一個元素。


完全線程版本(爲便於閱讀):

(->> (apply concat data) 
    (group-by key) 
    (map 
     (fn [[k vs]] 
     (if (next vs) 
      [k (mapv val vs)] 
      (first vs)))) 
    (into {})) 
0

有一個可預見的類型每個鍵可以節省當你想回去以後讀它你很頭疼,但如果你沒有其他的選擇:merge-with與自定義函數將解決它:

(apply merge-with (fn [v1 v2] ((if (vector? v1) conj vector) v1 v2)) data)