Join “是否有clojure函数?”;加入;两张地图清单?

Join “是否有clojure函数?”;加入;两张地图清单?,join,clojure,Join,Clojure,我正在寻找一个与sql中的join类似的join函数,例如: 以下是两张地图列表: (def a [{:user_id 1 :name "user 1"} {:user_id 2 :name "user 2"}]) (def b [{:user_id 2 :email "e 2"} {:user_id 1 :email "e 1"}]) 我希望在用户id上加入a和b以获得: [{:user_id 1 :name "user 1" :email "e 1"}

我正在寻找一个与sql中的join类似的join函数,例如:

以下是两张地图列表:

(def a [{:user_id 1 :name "user 1"} 
        {:user_id 2 :name "user 2"}])

(def b [{:user_id 2 :email "e 2"} 
        {:user_id 1 :email "e 1"}])
我希望在用户id上加入a和b以获得:

[{:user_id 1 :name "user 1" :email "e 1"} 
 {:user_id 2 :name "user 2" :email "e 2"}]

clojure或其他库中是否有一些函数可以实现这一点?

我认为没有任何简单的函数可以实现这一点,但我可能错了

如果您知道每个序列中都存在每个
user\u id
,则只需对
user\u id
进行排序,然后将合并应用于相应的映射:

(defn sort-by-user-id 
  [m]
  (sort #(< (:user_id %1) (:user_id %2)) m))

(map merge (sort-by-user-id a) (sort-by-user-id b))
; => ({:email "e 1", :name "user 1", :user_id 1} {:email "e 2", :name "user 2", :user_id 2})
然后像这样合并各个贴图,在过程结束时剥离关键点:

(vals (merge-with merge az bz))
; => ({:email "e 2", :name "user 2", :user_id 2} {:email "e 1", :name "user 1", :user_id 1})
总而言之:

(defn map-of-maps
  [cm]
  (zipmap (map :user_id cm) cm))

(defn merge-maps
  [& cms]
  (vals 
    (apply merge-with merge 
           (map map-of-maps cms))))
让我们确保它与缺少的
用户id
s一起工作:

(def a+ (conj a {:name "user 3", :user_id 3}))
(def b+ (conj b {:email "e 4", :user_id 4}))

(merge-maps a+ b+)
; => ({:email "e 4", :user_id 4} {:name "user 3", :user_id 3} {:email "e 2", :name "user 2", :user_id 2} {:email "e 1", :name "user 1", :user_id 1})

如果有更简单或更优雅的方法,我不会感到惊讶。这只是我想到的一个策略。

另一个选择,我觉得简单一点:

user=> (map #(apply merge %) (vals (group-by :user_id (concat a b))))
({:email "e 1", :name "user 1", :user_id 1} {:email "e 2", :name "user 2", :user_id 2})
groupby
创建从
:user\u id
到包含给定值的所有映射的映射,
vals
仅获取值(每个值都是一个向量),最后对于每个值向量,它们被合并。

将完成此操作

(use 'clojure.set)

(clojure.set/join a b) ; => #{{:email "e 1", :name "user 1", :user_id 1} {:email "e 2", :name "user 2", :user_id 2}}
在不提供第三个参数的情况下,函数将在所有公用键上联接:

(def a [{:id1 1 :id2 2 :name "n 1"} {:id1 2 :id2 3 :name "n 2"}])
(def b [{:id1 1 :id2 2 :url "u 1"} {:id1 2 :id2 4 :url "u 2"}])
(def c [{:id1 1 :id2 2 :url "u 1"} {:id1 2 :url "u 2"}]) ; :id2 is missing in 2nd record

(clojure.set/join a b) ; #{{:name "n 1", :url "u 1", :id1 1, :id2 2}}
(clojure.set/join a c) ; #{{:name "n 2", :url "u 2", :id1 2, :id2 3} {:name "n 1", :url "u 1", :id1 1, :id2 2}}
要仅在id1上加入a和b,请执行以下操作:

(clojure.set/join a b {:id1 :id1}) ; #{{:name "n 2", :url "u 2", :id1 2, :id2 4} {:name "n 1", :url "u 1", :id1 1, :id2 2}}
我们甚至可以通过来自不同集合的不同键进行连接:

(clojure.set/join a b {:id1 :id2}) ; #{{:name "n 2", :url "u 1", :id1 1, :id2 2}}

在我发布我的答案后,我注意到了。我不确定这个问题是否完全相同;不清楚询问的是什么,因为示例覆盖了一个键的值(
:b
,在包含
:a
2
的映射中)。然而,这个问题的答案就是你问题的答案。所以我想这算是一个重复的问题。因为我的答案不同,所以我把它放在这里了,但也许我应该把它贴到另一个问题上?不确定。谢谢!我发现clojure.set/join可以做到这一点。查找“set”的函数并不明显。clojure.set/join可以获得两个集合的Cartision乘积,还可以接受关键字对来连接它们。
(into{}(clojure.set/join a b))
返回映射。
(clojure.set/join a b {:id1 :id2}) ; #{{:name "n 2", :url "u 1", :id1 1, :id2 2}}