I am not to Clojure and attempting to figure out how to do this.
I want to create a new hash-map that for a subset of the keys in the hash-map applies a function to the elements. What is the best way to do this?
(let
[my-map {:hello "World" :try "This" :foo "bar"}]
(println (doToMap my-map [:hello :foo] (fn [k] (.toUpperCase k)))
This should then result a map with something like
{:hello "WORLD" :try "This" :foo "BAR"}
Breakdown:
It helps to look at it inside-out. In Clojure, hash-maps act like functions; if you call them like a function with a key as an argument, the value associated with that key is returned. So given a single key, the current value for that key can be obtained via:
We want to take old values, and change them to new values by calling some function
fon them. So given a single key, the new value will be:We want to associate this new value with this key in our hash-map, “replacing” the old value. This is what
assocdoes:(“Replace” is in scare-quotes because we’re not mutating a single hash-map object; we’re returning new, immutable, altered hash-map objects each time we call
assoc. This is still fast and efficient in Clojure because hash-maps are persistent and share structure when youassocthem.)We need to repeatedly
assocnew values onto our map, one key at a time. So we need some kind of looping construct. What we want is to start with our original hash-map and a single key, and then “update” the value for that key. Then we take that new hash-map and the next key, and “update” the value for that next key. And we repeat this for every key, one at a time, and finally return the hash-map we’ve “accumulated”. This is whatreducedoes.reduceis a function that takes two arguments: an “accumulator” value, which is the value we keep “updating” over and over; and a single argument used in one iteration to do some of the accumulating.reduceis the initial value passed as the first argument to thisfn.reduceis a collection of arguments to be passed as the second argument to thisfn, one at a time.So:
fn-to-update-values-in-our-mapis just theassocstatement from above, wrapped in an anonymous function:So plugging it into
reduce:In Clojure, there’s a shorthand for writing anonymous functions:
#(...)is an anonymousfnconsisting of a single form, in which%1is bound to the first argument to the anonymous function,%2to the second, etc. So ourfnfrom above can be written equivalently as:This gives us: