Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy way to filter list of maps by values of keys in Clojure

I cannot find build-in functionality in Clojure to filter list of maps by values of keys. For example we have a following list:

user=> (def animals (list {:name "Charlie" :weight 350 :specie :elephant}
                          {:name "Vasya" :weight 250 :specie :bear}
                          {:name "John" :weight 200 :specie :elephant}
                          {:name "Monk" :weight 200 :specie :monkey}))
#'user/animals

And I want to select, for example, all the :specie with value :elephant. How can I do that? The best way I found out is this:

user=> (defn filter-data [data m]
         (let [k (keys m) v (vals m)]
           (filter #(= (map % k) v) data)))
#'user/filter-data

user=> (clojure.pprint/print-table (filter-data animals {:specie :elephant}))

|   :name | :weight |   :specie |
|---------+---------+-----------|
| Charlie |     350 | :elephant |
|    John |     200 | :elephant |
nil

Is there a better way to do that?

like image 573
Artem Avatar asked Nov 17 '16 08:11

Artem


2 Answers

This could easily be achieved with filter

(clojure.pprint/print-table 
  (filter #(= (:specie %) :elephant) animals))

you can always wrap the filter in a function to get different "syntax", such as:

(defn cool-filter [[k v] l]
  (filter #(= (k %) v) l))

(clojure.pprint/print-table 
  (cool-filter [:specie :elephant] animals))
like image 79
Shlomi Avatar answered Nov 15 '22 04:11

Shlomi


While the filter solution is obviously the most straight-forward, I'd like to add an alternative in case you need to look up more than one species:

(def grouped-animals (group-by :specie animals))
(:elephant grouped-animals)
; [{:name "Charlie", :weight 350, :specie :elephant} {:name "John", :weight 200, :specie :elephant}]
like image 37
Michael Kohl Avatar answered Nov 15 '22 06:11

Michael Kohl