2014-10-29 81 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)