2016-04-26 79 views
2

這個函數有一組數字的向量,並返回它們的交集:如何通過clojure中的鍵找到交集的地圖集?

(defn foo [& sets] (apply clojure.set/intersection (map #(set %) sets))) 

例如。

user=> (foo [1 2 3] [3 4 5 1] [33 3 3 1]) 
#{1 3} 

你怎麼能達到同樣的事情,考慮到輸入集的元素與:id鍵映射到作爲地圖的獨特屬性中使用?

即。我試圖寫一個函數foo2這樣做:

user=> (foo2 [{:id 1 ...} {:id 2 ...} {:id 3 ...}] 
      [{:id 3 ...} {:id 4 ...} {:id 5 ...} {:id 1 ...}] 
      [{:id 33 ...} {:id 3 ...} {:id 3 ...} {:id 1 ...}]) 
[{:id 1 ...} {:id 3 ...}] 

有沒有一種簡單的方法來做到這一點?

或者是唯一的方法來收集ids,獲得鍵的交集,然後在輸入集中查找鍵?

是否有任何方法通過定義返回任意對象的鍵的「鍵值」函數來「重載」集合函數(聯合,交集等),還是隻能使用集合原始值?

+0

如何從給定的{:id 1 ...}地圖組成合成的{:id 1 ...}地圖? – Thumbnail

+0

@Thumbnail輸出映射應與輸入映射相同;它不是某種具有共同ID的各種地圖的彙總。在全部地圖不相同的情況下(即多個具有不同內容的ID),只要一個地圖在結果集中具有該ID,選擇哪一個並不重要。 – Doug

回答

2

編輯:我以前錯誤地認爲與相同的:id地圖保證具有相同的鍵和值。如果他們被允許有所不同,這是一個更好的答案:

(defn intersect-by [key-fn data] 
    (let [normalized (map (fn [map-data] 
          (->> map-data 
           (group-by key-fn) 
           (map (fn [[key val]] 
             [key (apply merge val)])) 
           (into {}))) 
         data) 
     normalized-keys (map (comp set keys) normalized) 
     intersection (apply clojure.set/intersection normalized-keys) 
     merged-data (apply merge-with merge normalized)] 
    (vals (select-keys merged-data intersection)))) 
#'user/foo 

(def data [[{:id 1} {:id 2} {:id 3, :x 3}] 
      [{:id 3, :y 4} {:id 4} {:id 5} {:id 1}] 
      [{:id 33} {:id 3} {:id 3, :z 5} {:id 1}]]) 
#'user/data 

(intersect-by :id data) 
({:id 1} {:id 3, :x 3, :y 4, :z 5}) 

如果假定兩個地圖與同:id都保證具有相同的鍵和值,你的第一個foo的功能已經做了這個:

(def data [[{:id 1} {:id 2} {:id 3}] 
      [{:id 3} {:id 4} {:id 5} {:id 1}] 
      [{:id 33} {:id 3} {:id 3} {:id 1}]]) 
#'user/data 

(defn foo [& sets] (apply clojure.set/intersection (map #(set %) sets))) 
#'user/foo 

(apply foo data) 
#{{:id 1} {:id 3}} 
+1

問題是關於具有相同ID的不同地圖,我猜,否則op不會問這個問題。 – leetwinski

+0

哎呀,我會修改我的答案 – ProjectFrank

2

我會做幾乎一樣:clojure.set/intersection,每個向量轉換後的排序條件爲:id設置:

user> (defn intersect [& colls] 
     (apply clojure.set/intersection 
       (map #(apply sorted-set-by 
          (fn [{id1 :id} {id2 :id}] (compare id1 id2)) 
          %) 
        colls))) 
#'user/intersect 

user> (intersect [{:id 1} {:id 2} {:id 3}] 
       [{:id 3} {:id 4} {:id 5} {:id 1}] 
       [{:id 33} {:id 3} {:id 3} {:id 1}]) 
#{{:id 1} {:id 3}} 

通知,這種做法會給予不同的地圖具有相同:id值相等,但我想這是你需要什麼。順便說一句,sorted-set正是「重載」你所問的設置函數的方式。

你可以做一個輔助函數,使它看起來更通用:

user> (defn key-comparator [key] 
     #(compare (key %1) (key %2))) 
#'user/key-comparator 

user> (defn intersect [& colls] 
     (apply clojure.set/intersection 
       (map #(apply sorted-set-by (key-comparator :id) %) 
        colls))) 
#'user/intersect 

user> (intersect [{:id 1} {:id 2} {:id 3 :x 1}] 
       [{:id 3 :x 10} {:id 4} {:id 5} {:id 1}] 
       [{:id 33} {:id 3 :x 33} {:id 3 :x 2} {:id 1}]) 
#{{:id 1} {:id 3, :x 33}} 

另一個變體是過濾從第一科爾,其ID存在於所有其他colls所有項目:

user> (defn intersect-2 [c & cs] 
     (if (empty? cs) c 
      (filter (fn [{id :id :as itm}] 
         (every? (partial some #(when (= id (:id %)) %)) 
           cs)) 
        c))) 
#'user/intersect-2 

user> (intersect-2 [{:id 1} {:id 2} {:id 3 :x 1}] 
        [{:id 3 :x 10} {:id 4} {:id 5} {:id 1}] 
        [{:id 33} {:id 3 :x 33} {:id 3 :x 2} {:id 1}]) 
({:id 1} {:id 3, :x 1}) 
+0

如果具有相同':id'值的地圖被允許具有其他不同的鍵和值,那麼如果這些其他鍵值對不是來自「贏」的地圖,那麼它們是否會丟失?例如在你的例子中,'{:id 3,:x 33}'正好勝過'{:id 3:x 2}'。如果'{:id 3:x 2}'改爲'{:id 3:x 2:y 3}',那麼'[:y 3 ['對將不會反映在結果中。 – ProjectFrank

+1

是的,你是對的。它真的取決於操作任務中的碰撞邏輯。由於我們沒有任何額外的信息,我認爲所有其他的關鍵是不重要的。但可能應該先問) – leetwinski