2014-02-28 76 views
0

什么是将一个地图矢量中的值添加到另一个具有与给定键相同的值的地图矢量的惯用方式,其中地图没有具有相同的密钥名称。 IE浏览器。Clojure:如何合并具有相同值的地图矢量

(def v1 
[{:name "name1" :address "address1"} 
{:name "name2" :address "address2"}]) 

(def v2 
[{:title "name1" :datofBirth "1-1-1971"} 
{:title "name3" :dateOfBirth "2-1-1971"}]) 

和合并的结果应该是

res 
[{:name "name1" :address "address1" :dateofBirth "1-1-1971"} 
{:name "name2" :address "address2" :dateOfBirth nil}] 

调用应该是这个样子

(join v1 v2 :name :title) 

而且身体应该是这个样子,也许assoc命令,在应该使用

(assoc-in v1 [(map-where-the-values-are-the-same) :key2] (value-from-the-2nd-map)) 
+0

是否有可能在相同的矢量上重复一个密钥?例如有超过一个元素:标题'name1'? –

+0

@GuillermoWinkler我不这么认为,但它有可能......数据来自API调用,所以它可能发生 – Vesna

回答

1

对于内部连接,你可以使用clojure.set/join

(clojure.set/join v1 v2 {:name :title}) 

你的样品结果似乎表明,要执行左连接,但是。如果是这样,你可能想看看Clojure中关于外连接的一些现有Stack Overflow问题;例如,我对最近的Full outer join two sequences of maps in Clojure问题的回答为完整的外部连接问题提供了一个高性能解决方案,它可以直接适用于生成左连接。

这里是一个可能的适应:

(defn left-join [key-map xs ys] 
    (let [kes (seq key-map) 
     lks (mapv key kes) 
     rks (mapv val kes) 
     gxs (group-by #(mapv (fn [k] (get % k)) lks) xs) 
     gys (dissoc (group-by #(mapv (fn [v] (get % v)) rks) ys) nil) 
     kvs (keys gxs)] 
    (persistent! 
    (reduce (fn [out kv] 
       (let [l (get gxs kv) 
        r (get gys kv)] 
       (if (seq r) 
        (reduce (fn [out m1] 
          (reduce (fn [out m2] 
             (conj! out (merge m1 m2))) 
            out 
            r)) 
          out 
          l) 
        (reduce conj! out l)))) 
      (transient []) 
      kvs)))) 

在REPL:

(left-join {:name :title} v1 v2) 
;= [{:name "name1", :datofBirth "1-1-1971", :title "name1", 
    :address "address1"} 
    {:name "name2", :address "address2"}] 

如果您想从所得到的地图而dissoc:title并从左侧的缺失项添加到记录对于其右侧没有相应的记录,可以稍微修改该功能,或者仅在后处理步骤中进行。

+0

@ michal-marzyk它看起来有些不错,但它并没有给出完整的第一个向量。实际上它留下的是外连接而不是内连接。我会看看你提供的问题。 – Vesna

+0

这是一个可能的左外连接实现。 –

1

最好使小函数然后使用线程宏调用。在波纹管中查找代码。

(def v1 [{:name "name1" :address "address1"} 
    {:name "name2" :address "address2"} 
    {:name "name4" :address "address2"}]) 

    (def v2 [{:title "name1" :datofBirth "1-1-1971"} 

    {:title "name3" :dateOfBirth "2-1-1971"} 
    {:title "name4" :dateOfBirth "2-1-1971"} 
    ]) 

(defn filter [k1 k2] 
    (fn [d1 d2] 
    (for [m1 d1 
      m2 d2 
     :let [n1 (k1 m1) 
      t1 (k2 m2)] 
     :when (= n1 t1) ] 
    (merge m1 (dissoc m2 k2))))) 


(defn data-join [k1] 
    (fn [d1 d2] 
    (reduce (fn [acc t] 
      (map (fn [v1 v2] 
       (if (= (k1 v1) 
         (k1 v2)) 
        v1 v2)) (repeat t) acc)) 
     d1 d2))) 

    (->> v2 
     ((filter :name :title) v1 ) 
     ((data-join :name) v1)) 

从v2过滤函数过滤数据。然后将结果与第一个结合。可以使两个功能都变得更小。

+0

这是一个很好的解决方案,我会研究它,但我更喜欢Michal的,因为对函数的调用更好 – Vesna

相关问题