loading...

So Many Ways to Update a Map with Elixir!

noelworden profile image Noel Worden Updated on ・3 min read

This Week I Learned (20 Part Series)

1) Beginning of a Blog Series 2) Accessing localhost within a Docker Image 3 ... 18 3) Identifying and Removing Hidden Characters 4) Getting Granular with Git Diff 5) Tweaking Logger Outputs on the Fly 6) Keeping Your Hands on the Keyboard: A Few Bash and Git Shortcuts 7) Breaking Down Elixir's `with` Expression 8) How To Write A Custom Elixir Schema Validation 9) The Handful of Commands I Use When Interactive Rebasing with Vim 10) Got 5 Minutes to Spare? Why Not Set up a Custom Commit Message Template? 11) Improving Your Commit Message with the 50/72 Rule 12) Simultaneously Looping Over Two Collections in Elixir 13) How to Utilize Enum.any?, with a Refactoring Twist! 14) A Rabbit Hole of Decimal Formatting 15) More Custom Validation Work: Manipulating a Keyword List To and From a Map 16) Stepped Away for a Long Weekend 17) So Many Ways to Update a Map with Elixir! 18) Git: Keeping Files from Being Tracked Without .gitignore 19) Git: Tracking Down a Committed File, with a Real-Time Ah-Ha Moment! 20) Setting up `.iex.exs` to Speed up Your Elixir Debugging (and How to Include It in a Docker Image)

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.

This Week I Learned (20 Part Series)

1) Beginning of a Blog Series 2) Accessing localhost within a Docker Image 3 ... 18 3) Identifying and Removing Hidden Characters 4) Getting Granular with Git Diff 5) Tweaking Logger Outputs on the Fly 6) Keeping Your Hands on the Keyboard: A Few Bash and Git Shortcuts 7) Breaking Down Elixir's `with` Expression 8) How To Write A Custom Elixir Schema Validation 9) The Handful of Commands I Use When Interactive Rebasing with Vim 10) Got 5 Minutes to Spare? Why Not Set up a Custom Commit Message Template? 11) Improving Your Commit Message with the 50/72 Rule 12) Simultaneously Looping Over Two Collections in Elixir 13) How to Utilize Enum.any?, with a Refactoring Twist! 14) A Rabbit Hole of Decimal Formatting 15) More Custom Validation Work: Manipulating a Keyword List To and From a Map 16) Stepped Away for a Long Weekend 17) So Many Ways to Update a Map with Elixir! 18) Git: Keeping Files from Being Tracked Without .gitignore 19) Git: Tracking Down a Committed File, with a Real-Time Ah-Ha Moment! 20) Setting up `.iex.exs` to Speed up Your Elixir Debugging (and How to Include It in a Docker Image)

Posted on by:

noelworden profile

Noel Worden

@noelworden

Software Engineer in Boulder, CO - Writing code and getting strategically lost in the mountains

Discussion

markdown guide
 

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