【问题标题】:Clojure: How to merge vector of maps that have the same valueClojure:如何合并具有相同值的地图向量
【发布时间】:2014-02-28 13:52:52
【问题描述】:

将值从一个映射向量添加到另一个具有给定键值的映射向量的惯用方法是什么,其中映射没有相同的键名称。 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)

body 应该是这样的,也许应该使用 assoc-in

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

【问题讨论】:

  • 一个键可以在同一个向量上重复吗?例如有多个元素 :title name1?
  • @GuillermoWinkler 好吧,我不这么认为,但有可能...数据来自 API 调用,所以它可能会发生

标签: vector clojure merge maps


【解决方案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 并将缺少的键添加到左侧没有相应记录的记录中,您可以稍微修改函数或只是在后处理步骤中进行。

【讨论】:

  • @michal-marzyk 看起来还不错,但它没有给出完整的第一个向量。实际上它是左外连接而不是内连接。我会看看你提供的问题。
  • 这是一种可能的左外连接实现。
【解决方案2】:

最好先创建小函数,然后使用线程宏调用。在下面找到代码。

 (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))

Filter 函数过滤来自 v2 的数据。然后将结果与第一个连接起来。可以使这两个功能都变小。

【讨论】:

  • 这是一个很好的解决方案,我会研究它,但我更喜欢 Michal 的,因为对函数的调用要好得多
猜你喜欢
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多