This post is an experiment in stream of consciousness programming. I will attempt to recreate my thought process, even errors. I believe this may be more useful to my future self than having perfect code and wondering how I ever came up with that. Plus it gives me an excuse to write less than prefect posts and get back to attaining vision. So here goes.
The other day I needed to update the values of specific keys in a map. I wanted to apply a function to the values of these keys.
As an example, say we have the following map:
We want to apply an arbitrary function, say
inc, to the values of keys
:b, resulting in the following map:
Googling a bit, I found a post by Jay Fields, Clojure: Apply a Function To Each Value of a Map, that did almost what I needed, but not quite. I don’t want to apply the function to all values, just a specific subset.
Next I searched clojure.core.
update-in seem to be the closest thing to what we’re looking for.
Let’s try it out.
That didn’t quite work. Let’s take a look at the docs.
This updates a nested associative array, not exactly what we need.
Hmm. That won’t do.
Switching gears, maybe we can create a vector of updated key-values and
assoc them onto the existing map.
Let’s take all the keys we want to update the values of, create a vector of
[key updated-value] for each and
assoc them onto the original map.
So we have a seq of
kvs, now let’s
assoc the mappings to our original map.
This works, but there has to be a better way.
Let’s try to get rid of the ugly mapcat-vector calls. Let’s create a function for updating key-value pairs and use a for-comprehension.
Oops. Let’s flatten that out.
That gives us what we want, but depending on your tastes, may be more complicated than the original.
I’m not sure
is better than
Pick your poison.
Next try: Instead of creating a vector of key-values, let’s have map-keys create a map and then we can merge it with the existing map.
This gives us the right answer, but we can do better.
Let’s add the
merge into the function to make it cleaner.
Let’s add optional args to function to make it more useful.
Perfect. I know this is perfectly good Clojure code. We are iterating over the keys and only applying the function to those keys.
From looking at the definition of
map-keys, what we are actually doing is iterating over keys.
So let’s go back to
If we think about it, what we really want is to call
on each key and accumulate the results.
Ding. Ding. Ding. What we wanted all along was
Now let’s clean it up a little to accept functions with parameters.
There are many ways to implement updating keys, there is no right way. I prefer the reduce variant as I believe it is more concise.
I finally hit upon the
reduce variant when I stepped back and and saw that what I really wanted was to call
update-in many times and accumulate the results.
As a rule of thumb, whenever I think, “Man, that function is almost what I need, I just wish I could apply it to multiple arguments separately and compose the results”, I start to think the situation may be calling for a