Clojure:如何合并具有相同值的地图矢量

时间:2014-02-28 13:52:52

标签: vector clojure merge maps

将一个地图矢量的值添加到具有相同给定键值的另一个地图矢量的地道方法是什么,其中地图不具有相同的键名称。 IE浏览器。

(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-in v1 [(map-where-the-values-are-the-same) :key2] (value-from-the-2nd-map))

2 个答案:

答案 0 :(得分: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并将缺失的密钥添加到左侧的记录中,而右侧没有相应的记录,则可以修改稍微运行或只是在后处理步骤中执行。

答案 1 :(得分: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过滤功能过滤数据。然后用第一个加入结果。可以使这两个功能都变小。