Clojure get map key by value - clojure

I'm a new clojure programmer.
Given...
{:foo "bar"}
Is there a way to retrieve the name of the key with a value "bar"?
I've looked through the map docs and can see a way to retrieve key and value or just value but not just the key. Help appreciated!

There can be multiple key/value pairs with value "bar". The values are not hashed for lookup, contrarily to their keys. Depending on what you want to achieve, you can look up the key with a linear algorithm like:
(def hm {:foo "bar"})
(keep #(when (= (val %) "bar")
(key %)) hm)
Or
(filter (comp #{"bar"} hm) (keys hm))
Or
(reduce-kv (fn [acc k v]
(if (= v "bar")
(conj acc k)
acc))
#{} hm)
which will return a seq of keys. If you know that your vals are distinct from each other, you can also create a reverse-lookup hash-map with
(clojure.set/map-invert hm)

user> (->> {:a "bar" :b "foo" :c "bar" :d "baz"} ; initial map
(group-by val) ; sorted into a new map based on value of each key
(#(get % "bar")) ; extract the entries that had value "bar"
(map key)) ; get the keys that had value bar
(:a :c)

As in many other cases you can use for:
(def hm {:foo "bar"})
(for [[k v] hm :when (= v "bar")] k)
And with "some" you can return the first matching item instead of a list (as probably the original question implied):
(some (fn [[k v]] (if (= v "bar") k)) hm)

Related

Clojure: convert vector of maps into map of maps with incremented keys

I have a vector of maps
cards_vector = [{...} {...} ...]
and an atom
(def cards_map (atom {})
For each map in cards_vector, I want to add the map to cards_map with key card-n, where n increments from 1 to count(cards_vector). So, cards-map should return
{:card-1 {...}
:card-2 {...}
...
:card-n {...}}
I propose this snippet:
(->> [{:a 1} {:b 2}]
(map-indexed (fn [idx value] [(keyword (str "card-" idx)) value]))
(into {}))
;; => {:card-0 {:a 1}, :card-1 {:b 2}}
But I agree with the comment of cfrick. Choosing a key with the shape :card-X doesn't seem to be really practical. But you can do it :)
Another solution, closer to imperative programming but maybe less efficient than map-indexed:
(into {} (for [k (range (count #cards_map))] [(keyword (str "card-" k)) (nth #cards_map k)]))

Clojure - Create an array of keys if value is true

I am totally new to clojure.
I have a JSON like: { "1": true, "2": false, "3": true, "4": false }
I want to create an array of keys for which the value is true in clojure. In this example the array should be ["1", "3"].
Please help me. Any help would be appreciated.
there are also couple of short and simple snippets for that:
user> (filter m (keys m))
;;=> ("1" "3")
user> (keep (fn [[k v]] (when v k)) m)
;;=> ("1" "3")
user> (for [[k v] m :when v] k)
;;=> ("1" "3")
If you're fine with using a vector instead of an array (since you're usually using vectors in Clojure anyway), you can do something like.
(defn keys-for-truthy-vals [m]
(->> m (filter val) (mapv key)))
Note The mapv is only so the map call returns a vector. If you want a seq, just use map.
The same as already provided, just staying in maps.
(keys (filter val m))
If your map is a Something like (->> (filter (fn [[k v]] v) a) (map (fn [[k v]] k))) will work. You can't do it with just a map because you need to drop certain values, so there will need to be some reducing or filtering.
There is built-in function in the Tupelo library for this:
(submap-by-vals map-arg keep-vals & opts)
Returns a new map containing entries with the specified vals. Throws for missing vals,
unless `:missing-ok` is specified. Usage:
(submap-by-vals {:a 1 :b 2 :A 1} #{1 } ) => {:a 1 :A 1}
(submap-by-vals {:a 1 :b 2 :A 1} #{1 9} :missing-ok ) => {:a 1 :A 1}
You could then just use the keys function on the resulting map.
Maybe this?
(->> foo (filter second) keys)
where foo is a map.

Get key by first element in value list in Clojure

This is similar to Clojure get map key by value
However, there is one difference. How would you do the same thing if hm is like
{1 ["bar" "choco"]}
The idea being to get 1 (the key) where the first element if the value list is "bar"? Please feel free to close/merge this question if some other question answers it.
I tried something like this, but it doesn't work.
(def hm {:foo ["bar", "choco"]})
(keep #(when (= ((nth val 0) %) "bar")
(key %))
hm)
You can filter the map and return the first element of the first item in the resulting sequence:
(ffirst (filter (fn [[k [v & _]]] (= "bar" v)) hm))
you can destructure the vector value to access the second and/or third elements e.g.
(ffirst (filter (fn [[k [f s t & _]]] (= "choco" s))
{:foo ["bar", "choco"]}))
past the first few elements you will probably find nth more readable.
Another way to do it using some:
(some (fn [[k [v & _]]] (when (= "bar" v) k)) hm)
Your example was pretty close to working, with some minor changes:
(keep #(when (= (nth (val %) 0) "bar")
(key %))
hm)
keep and some are similar, but some only returns one result.
in addition to all the above (correct) answers, you could also want to reindex your map to desired form, especially if the search operation is called quite frequently and the the initial map is rather big, this would allow you to decrease the search complexity from linear to constant:
(defn map-invert+ [kfn vfn data]
(reduce (fn [acc entry] (assoc acc (kfn entry) (vfn entry)))
{} data))
user> (def data
{1 ["bar" "choco"]
2 ["some" "thing"]})
#'user/data
user> (def inverted (map-invert+ (comp first val) key data))
#'user/inverted
user> inverted
;;=> {"bar" 1, "some" 2}
user> (inverted "bar")
;;=> 1

how to destructure a map as key and value

Is there a way to destructure a key value pair ? I have a function which take a map as a parameter, I would like to extract the value of both the key and the value in the params itself. How do I do that ?
I can do the following with a vector -
((fn [[a b]] (str a b)) [a b])
How do I do the same / similar with map -
((fn[{k v}] (str k v)) {k v})
Thanks,
Murtaza
map destructuring in functions arg lists is designed for extracting certain keys from a map and giving them names like so:
core> (defn foo [{my-a :a my-b :b}] {my-a my-b})
core/foo
core> (foo {:a 1 :b 2})
{1 2}
i recommend this tutorial. It is a little hard to give a direct equivalent to ((fn[{k v}] (str k v)) {k v}) because the map could have many keys and many values so the destructuring code would be unable to tell which key and value you where looking for. Destructuring by key is easier to reason about.
If you want to arbitrarily choose the first entry in the map you can extract it and use the list destructuring form on a single map entry:
core> (defn foo [[k v]] {v k})
#'core/foo
core> (foo (first {1 2}))
{2 1}
in this example the list destructuring form [k v] is used because first returns the first map entry as a vector.
There are shortcuts available for destructuring maps. For example, if you're looking for specific keys, then you don't have to type out name1 :key1 name1 :key2...
e.g.
main=> (defn fbb [{:keys [foo bar baz]}] (+ foo bar baz))
#'main/fbb
main=> (fbb {:foo 2 :bar 3 :baz 4})
9
instead of...
(defn fbb [{foo :foo bar :bar baz :baz}] (+ foo bar baz))
If your map keys are strings, you can say :strs instead of :keys and if they are symbols you can use :syms.
user=> (for [x (hash-map :a 1 :b 2 :c 3)] (str (first x) " " (second x)))
(":a 1" ":c 3" ":b 2")

Dissoc multiple descendent keys of a map?

How can I search and dissoc multiple descendent keys.
Example:
(def d {:foo 123
:bar {
:baz 456
:bam {
:whiz 789}}})
(dissoc-descendents d [:foo :bam])
;->> {:bar {:baz 456}}
clojure.walk is useful in this kind of situations:
(use 'clojure.walk)
(postwalk #(if (map? %) (dissoc % :foo :bam) %) d)
If you wanted to implement it directly then I'd suggest something like this:
(defn dissoc-descendents [coll descendents]
(let [descendents (if (set? descendents) descendents (set descendents))]
(if (associative? coll)
(reduce
(fn [m [k v]] (if (descendents k)
(dissoc m k)
(let [new-val (dissoc-descendents v descendents)]
(if (identical? new-val v) m (assoc m k new-val)))))
coll
coll)
coll)))
Key things to note about the implementation:
It makes sense to convert descendents into a set: this will allow quick membership tests if the set of keys to remove is large
There is some logic to ensure that if a value doesn't change, you don't need to alter that part of the map. This is quite a big performance win if large areas of the map are unchanged.