how to destructure a map as key and value - clojure

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")

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)]))

Using Java hashmaps in Clojure

I am new to Clojure. I am trying to use java hashmap in clojure. I am passing a java hashmap to Clojure. The map is- {0=Goa, 1=Delhi, 2=Mumbai}. When I am trying to use the clojure functions on this map I am not getting the expected output. In contrast to this when I am iterating over this map it is giving the expected output.
Example
(println(get map 0)) is giving nil
(doseq [[key value] map
(println value)) is giving the expected output.
Output-Goa
Delhi
Mumbai
Can somebody please expain me why is this happening?
You really should google a bit to find pre-existing answers like this one: Clojure: working with a java.util.HashMap in an idiomatic Clojure fashion
You can then see a simple answer:
(def data {:a 1 :b 2 :c 3})
(def java-map (java.util.HashMap. data))
(def clj-map (into {} java-map))
which gives us:
java-map => <#java.util.HashMap {:b 2, :c 3, :a 1}>
clj-map => <#clojure.lang.PersistentArrayMap {:b 2, :c 3, :a 1}>
and looping:
(doseq [[k v] clj-map]
(println (format "key=%s val=%s" k v)) )
with result:
key=:b val=2
key=:c val=3
key=:a val=1
I think your problem is that your map is named "map" which is also a Clojure function. Try it like this:
(def my-map {0 "Goa" 1 "Delhi" 2 "Mumbai"})
Which then will work like this:
(println (get my-map 0))
Note that it still returns nil, since there is nothing else after the (println) form but it does print the value of the 0 in the map, which is "Goa".
(def input-map {0 "Goa" 1 "Delhi" 2 "Mumbai"})
(map (fn[[k v]] (print "key " k " value " k)) input-map)
[[k v]] for function let you access key and value for each entry
(map print input-map)
here map entry will be passed as parameter to print

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.

Clojure get map key by value

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)

Better way to nest if-let in clojure

Say I have a map of this form:
(def m {:a "A" :b "B"})
and I want to do something if :a and :b are both not nil, I can do:
(if-let [a (:a m)]
(if-let [b (:b m)]
... etc ))
or
(if (and (:a m) (:b m))
(let [{a :a b :b} m]
... etc ))
or even
(if (every? m [:a :b])
(let [{a :a b :b} m]
... etc ))
Is there a neater (ie one-line) way to achieve this?
I think a macro may be necessary here to create the behavior you want. I have never written one (yet) but the following representation suggests to me that this might be fairly straightforward:
(let [{:keys [a b]} m]
(when (every? identity [a b])
(println (str "Processing " a " and " b))))
Using the :keys form of destructuring binding and every? enables a single specification of a vector of keys to destructure and check, and the bound locals are available in a following code block.
This could be used to make a macro such as (when-every? [keys coll] code-with-bindings)
I may update this answer with the macro code if I can take the time to work out how to do it.
You could use map destructuring -- a useful feature of Clojure. This also exploits the facts that and is short-circuiting, and any key in the first map not found in the second map gets nil, a falsy value:
(let [{a :a b :b} {:a 1 :b "blah"}]
(and a b (op a b)))
Okay, so it's two lines instead of one .... also this doesn't distinguish between nil and other falsy values.
not-any? is a nice shortcut for this:
user> (not-any? nil? [(m :a) (m :b)])
true
user> (not-any? nil? [(m :a) (m :b) (m :d)])
false
user>
I am not quite sure what you want to do if the keys have non-nil values or whether you want non-nil keys or values returned. So, I just solved it for non-nil keys being returned.
You'd use the following as an intermediate step as part of a final solution.
I'm showing all the steps I used, not to be pedantic, but to provide a complete answer. The namespace is repl-test. It has a main associated with it.
repl-test.core=> (def m {:a "A" :b "B" :c nil})
#'repl-test.core/m
repl-test.core=> (keys m)
(:a :c :b)
and then finally:
; Check key's value to determine what is filtered through.
repl-test.core=> (filter #(if-not (nil? (%1 m)) (%1 m)) (keys m) )
(:a :b)
By the way I found an ugly one-liner, which works because and returns the last thing in its argument list if they're all true:
(if-let [[a b] (and (:a m) (:b m) [(:a m)(:b m)])]
(println "neither " a " nor " b " is falsey")
(println "at least one of " a " or " b " is falsey"))