edit-chi-ferris

Elixir Exercises for the Rubyist, Part 1… Lists and Maps

Lately I’ve been spending some time learning Elixir. But sometimes at the end of the day I’m not really intellectually up for reading 40 pages of Dave Thomas’s (fantastic) book.

I remember when I was learning Ruby, I found Learn Ruby the Hard Way to be a helpful resource. Sometimes rote work is a good way to gain a comfort level with a language.

So I’m writing these exercises to give myself something to do when I while watching TV or otherwise unwinding.

I spend a good bit of my time in Ruby throwing around arrays of hashes.
So I’m gonna figure out how to do the following in Elixir:

1. Create a map
2. Add another key-value pair to the existing map
3. Add several maps to a list
4. Map the list to the values of a certain key
5. Filter the list

1. Create a map

Now, I like IPAs; the Indian Pale Ale is my favorite type of beer. I’m gonna use a few of those as my examples.

Fortunately, maps have pretty similar syntax to Ruby’s hash, just add the ‘%’ at the beginning of the declaration.

good_beer = %{ name: "Hopslam", brewery: "Bells" }
# => %{brewery: "Bells", name: "Hopslam"}

2. Add another key-value pair to the existing map

We have a nice representation of a beer, but you know what, I probably want to add the ABV as well. How do I add another value to the map?

I don’t feel it’s as intuitive as Ruby in this regard. Part of that though, might be getting used to starting the function calls with a module. In Ruby, I’ve gotten used to starting method calls with the object, but here’s an example of starting the function call with the module: Map.put(map, key, val)

favorite_beer = Map.put good_beer, :abv , 10
# => %{abv: 10.0, brewery: "Bells", name: "Hopslam"}

De-referencing the maps should be pretty familiar to a Ruby developer.
You can use a syntax very similar to Ruby hashes.

favorite_beer[:brewery]
# => "Bells"

But you can also use dot notation, similar to the Hashie library but without the overhead.

favorite_beer.brewery
# => "Bells"

One thing to note, they don’t behave the same way with unfound keys

favorite_beer[:color]
# => nil

versus

favorite_beer.color
# ** (KeyError) key :color not found in: %{abv: 10, brewery: "Bells", name: "Hopslam"}

Anyway, back to building our list.
second_favorite_beer = %{ name: "Hoptimum", brewery: "Sierra Nevada", abv: 10.04 }
# => %{abv: 10.04, brewery: "Sierra Nevada", name: "Hoptimum"}

third_favorite_beer = %{ name: "Two Hearted Ale", brewery: "Bells", abv: 7.0 }
# => %{abv: 7.0, brewery: "Bell's", name: "Two Hearted Ale"}

3. Add several maps to a list

beer_list = [ favorite_beer, second_favorite_beer, third_favorite_beer ]

# => [%{abv: 10, brewery: "Bells", name: "Hopslam"},
%{abv: 10.04, brewery: "Sierra Nevada", name: "Hoptimum"},
%{abv: 7.0, brewery: "Bells", name: "Two Hearted Ale"}]

4. Map the list to the values of a certain key

The Enum module will be real handy here.

beer_names = Enum.map(beer_list, fn x -> x.name end)
# => ["Hopslam", "Hoptimum", "Two Hearted Ale"]

5. Filter the list

Enum.filter is pretty much Ruby’s #select/#collect.

bells_beers = Enum.filter(beer_list, fn x -> x.brewery == "Bells" end)
# => [%{abv: 10, brewery: "Bells", name: "Hopslam"},
# %{abv: 7.0, brewery: "Bells", name: "Two Hearted Ale"}]

Cool. I now have a handful of expressions I can practice while watching the Home Run Derby. Giancarlo is crushing it.