Ash
Ash

Reputation: 2601

NOT - EXISTS / NOT - IN type query in Clojure

I have 2 data structures like the ones below

(ns test)

(def l 
  [{:name "Sean" :age 27} 
   {:name "Ross" :age 27}
   {:name "Brian" :age 22}])

(def r 
  [{:owner "Sean" :item "Beer" }
   {:owner "Sean" :item "Pizza"}
   {:owner "Ross" :item "Computer"} 
   {:owner "Matt" :item "Bike"}])

I want to have get persons who dont own any item . (Brian in this case so [ {:name "Brian" :age 22}] If this was SQL I would do left outer join or not exists but I not sure how to do this in clojure in more performant way.

Upvotes: 1

Views: 326

Answers (2)

Michał Marczyk
Michał Marczyk

Reputation: 84331

While Chuck's solution is certainly the most sensible one, I find it interesting that it is possible to write a solution in terms of relational algebraic operators using clojure.set:

(require '[clojure.set :as set])
(set/difference (set l)
                (set/project (set/join r l {:owner :name})
                             #{:name :age}))
; => #{{:name "Brian", :age 22}}

Upvotes: 4

Chuck
Chuck

Reputation: 237020

You basically want to do a filter on l, but negative. We could just not the condition, but the remove function already does this for us. So something like:

(let [owner-names (set (map :owner r))]
  (remove #(owner-names (% :name)) l))

(I think it reads more nicely with the set, but if you want to avoid allocating the set, you can just do (remove (fn [person] (some #(= (% :owner) (person :name)) r)) l).)

Upvotes: 4

Related Questions