# Filtering vector of maps based on a blacklist

Given a vector of maps:

(def my-list
[{:a 1 :b 2 :c 3}
{:a 1 :b 5 :c 6}
{:a 7 :b 8 :c 12}
{:a 10 :b 11 :c 12}])


and a blacklist:

(def blacklist
[{:a 1 :c 3}
{:a 10 :c 12}])


the expected output is:

[{:a 1 :b 5 :c 6}
{:a 7 :b 8 :c 12}]


I came up with:

(defn not-excluded?
[blacklist item]
(not-any? (fn [exclusion-item]
(and (= (:a item) (:a exclusion-item))
(= (:c item) (:c exclusion-item))))
blacklist))

(filter (partial not-excluded? blacklist) my-list)


but there is probably a more idiomatic Clojure way.

• I don't Clojure very well, but requiring :a and :c to be hardcoded in the function doesn't look like a good solution. Have you considered passing those as arguments instead?
– Mast
Nov 26, 2019 at 15:01
• Yeah, that's one thing, that can be improved for sure. 👍 Nov 26, 2019 at 15:54

We want to exclude from the vector (more generally a sequence) the maps of which any map in the blacklist is a sub-map. Let's define a function that tests whether its first argument is a sub-map of its second argument:

(defn submap? [a b]
(every? (fn [[k v]] (= v (b k)))  a))


For instance,

=> (map #(submap? {1 2} %) [{} {1 1} {1 2}])
(false false true)


So far, so good. Let's use submap? to define function clean that takes a blacklist and a sequence of maps as arguments, excluding any supermaps of any map in the blacklist from the sequence:

(defn clean [blacklist ms]
(filter
(fn [m] (not-any? #(submap? % m) blacklist))
ms))


For example,

=> (clean blacklist my-list)
({:a 1, :b 5, :c 6} {:a 7, :b 8, :c 12})


submap? could be careful about absent keys. If you have nil values, you need to fix this.