Search code examples
clojuresetcomparecomparatorset-union

Clojure: how to use compare with set/union


For the sake of example, let's assume I have two sets:

(def set-a #{{:id 1 :name "ABC" :zip 78759} {:id 2 :name "DEF" :zip 78759}})

(def set-b #{{:id 1 :name "ABC" :zip 78753} {:id 3 :name "XYZ" :zip 78704}})

I would like to find an union between the sets, using only :id and :name fields. However, with out using a custom comparator I get four elements in the set, because :zip field is different.

(clojure.set/union set-a set-b)

#{{:id 3, :name "XYZ", :zip 78704} {:id 1, :name "ABC", :zip 78753}
  {:id 1, :name "ABC", :zip 78759} {:id 2, :name "DEF", :zip 78759}}

What is the idomatic way of finding union between two sets using a custom comparator or compare?


Solution

  • You could use group-by to do this:

    (map first (vals (group-by (juxt :id :name) (concat set-a set-b))))
    

    Or threaded:

    (->> (concat set-a set-b)
         (group-by (juxt :id :name))
         (vals)
         (map first))
    

    This is grouping your elements by a combination of their key/values i.e. (juxt :id :name). Then it grabs the values of the produced map, then maps first over that to get the first item in each grouping.

    Or use some code specifically built for this like distinct-by.

    Note these approaches apply to any collection, not just sets.