Home > Software engineering >  clojure - presence of a given set of keys in a clojure map
clojure - presence of a given set of keys in a clojure map

Time:08-24

There is a map like this

{:buyers [{:name "James" :city "Berlin"} {:name "Jane" :city "Milan"}]
:sellers [{:name "Dustin" :city "Turin" :age "21"} {:name "Mark" :city "Milan"}]}

and I need to check only for :sellers that all the keys :name, :city and :age are present and if one is missing drop that map all together and have a new structure as below:

{:buyers [{:name "James" :city "Berlin"} {:name "Jane" :city "Milan"}]
:sellers [{:name "Dustin" :city "Turin" :age "21"}]}

CodePudding user response:

(let [data {:buyers  [{:name "James" :city "Berlin"} {:name "Jane" :city "Milan"}]
            :sellers [{:name "Dustin" :city "Turin" :age "21"} {:name "Mark" :city "Milan"}]}]
    (update data :sellers #(filter (every-pred :name :city :age) %)))

CodePudding user response:

To make the code more readable, I created a new predicate, valid-seller?, and put validation there. You can use any of these versions:

Pure Clojure:

(defn valid-seller? [m]
  (every? #(contains? m %) [:name :city :age]))

Spec:

[org.clojure/spec.alpha "0.3.218"], require [clojure.spec.alpha :as spec]

(defn valid-seller? [m]
  (spec/valid? (spec/keys :req-un [::name ::city ::age]) m))

Malli (if you also want to test type of values):

[metosin/malli "0.8.9"], require [malli.core :as malli]

(defn valid-seller? [m]
  (malli/validate [:map
                   [:name :string]
                   [:city :string]
                   [:age :string]] m))

Then I used this predicate:

(update {:buyers [{:name "James" :city "Berlin"} {:name "Jane" :city "Milan"}]
         :sellers [{:name "Dustin" :city "Turin" :age "21"} {:name "Mark" :city "Milan"}]}
        :sellers
        #(filter valid-seller? %))
=>
{:buyers [{:name "James", :city "Berlin"} {:name "Jane", :city "Milan"}],
 :sellers ({:name "Dustin", :city "Turin", :age "21"})}

After your answer, I think you should use Malli, as it also checks the type of values. You can use some? for any non-nil value:

(defn valid-seller? [m]
  (malli/validate [:map
                   [:name some?]
                   [:city some?]
                   [:age some?]] m))
  • Related