Reputation: 7223
Say I have a map m
like {"good1" 1, "bad1" 1, "good2" 2, "bad2" 2}
, and I'd like to remove entries based on some predicate over the keys of the map, one way to do this would be:
(defn dissoc-by [f m] (->> m (filter (complement f)) (into {})))
(dissoc-by #(.contains (first %1) "bad") m)
=> {"good1" 1, "good2" 2}
Is there a more idiomatic way of doing this in clojure?
Upvotes: 3
Views: 2101
Reputation: 1679
Here is the code that does exactly what you need:
(def data {"good1" 1, "bad1" 1, "good2" 2, "bad2" 2})
(into (empty data)
(filter (fn [[k v]] (clojure.string/includes? k "good"))
data))
{"good1" 1, "good2" 2}
Upvotes: 1
Reputation: 13483
Given
(def data {"good1" 1, "bad1" 1, "good2" 2, "bad2" 2})
define
(defn remove-keys [pred m]
(apply dissoc m (filter pred (keys m))))
then
(remove-keys #(string/includes? % "bad") data)
=> {"good1" 1, "good2" 2}
Similar functions are defined in/at The Clojure Cookbook.
Upvotes: 7
Reputation: 91617
This is a very normal way of going about it, with the one correction that a sequence from a map is a sequence of pairs [key value] rather than just the keys, so your filter function needs to use the key explicitly
user> (defn dissoc-by [f m] (->> m (filter #(f (first %))) (into {})))
#'user/dissoc-by
user> (dissoc-by #(.contains % "good") m)
{"good1" 1, "good2" 2}
If you would like to get fancy about it and use a transducer this function can be made more efficient by eliminating the intermediate sequences that are allocated to move data from one step to the next.
user> (defn dissoc-by [f m]
(into {} (filter #(f (first %))) m))
#'user/dissoc-by
user> (dissoc-by #(.contains % "good") m)
{"good1" 1, "good2" 2}
into
takes an optional middle argument, a transducer function, that can filter or transform the data as it is pored into the collection.
Upvotes: 5