Last week I got a pleasant refresher on updating maps. As I type this it sounds like such a basic thing, updating a map, but I guess I haven't done it all that much in this project. Maps are used everywhere, but for the most part I'm taking existing maps and not really manipulating them, just passing them along to be used in an insert or update function.
In my case I actually wanted to update a map before it got to the changeset, to change the shape of the data. It's actually a follow-up from the work I did around this post. Not being able to recall from memory how to update a map, I went to the ol' search engine, and that's where I came across this little gem:
It's so elegant it honestly made me smile. Now, there are of course other ways to update a map, from the docs here are four:
Since I learn by example, I'll write them all out, starting with a map to work with:
Let's start with the shorthand syntax, if the key exists, it is updated:
And if the key doesn't already exist in the map, it will throw an error:
Next is Map.put/3
, it's pretty straightforward, if the key already exists it updates the value:
Or, if the key doesn't exist, it adds the new key value pair to the map:
Then there is Map.replace!/4
. This one replaces the value stored, but only if the key exists in the map, otherwise a KeyError
exception is raised.
The happy path:
And when the key is not present in the map:
Then there is Map.update!/3
, notice the bang (!
). This updates the key with the provided function, if the key isn't present it raises a KeyError
exception.
Adding to the existing string of :charlie
:
And when the :delta
key doesn't exist in the map:
And, last but not least, Map.update/4
. This is similar to the non-bang version, but instead of throwing an error if the key doesn't exist, it adds it to the map:
One more note about this function, if the key does not exist in the map, it will be added and assigned the value provided as the third argument, and the function provided as the fourth argument will be ignored:
Again, since the key delta:
did not already exist, it used the third argument "four"
as the value, and did not utilize the function at all.
These are all great, and have their unique use cases, but one drawback they all have is that if several keys need to be updated, the only way to do that is to repeat the function with piping:
And that is where the shorthand syntax really shines, multiple fields can be updated in the one expression:
So clean! 😍
As I noted at the beginning of this post, I needed to intercept and update the attrs
before they were sent to the changeset
, in order to cast some data into a different shape. My code to do that looks like this:
It's not often that it's necessary to sneak into the attrs
to tweak data before it gets inserted into the database. As a matter of fact it might be a code smell to some, but it was necessary in this case. I hope this post comes in handy the next time you find yourself needing to update a map, regardless of the scenario.
This post is part of an ongoing This Week I Learned series. I welcome any critique, feedback, or suggestions in the comments.
Top comments (1)
Funnily enough, I similarly called it the "short syntax", and had completely forgotten the official name for using the "pipe" (not to be confused with
|>
). The|
in Elixir and Erlang is the cons operator and most often used by us in lists and maps. I've not looked into the origins of that name, but do know that it exists in various functional languages with a different symbol or denoted in some other way