Prev Next

Part 1

Part 2

Slicing and Dicing with Enum in Elixir

A 10-Pack of Bears 🐻

Here’s the list of bears we used in the video:

def list_bears do
  [
    %Bear{id: 1, name: "Teddy", type: "Brown", hibernating: true},
    %Bear{id: 2, name: "Smokey", type: "Black"},
    %Bear{id: 3, name: "Paddington", type: "Brown"},
    %Bear{id: 4, name: "Scarface", type: "Grizzly", hibernating: true},
    %Bear{id: 5, name: "Snow", type: "Polar"},
    %Bear{id: 6, name: "Brutus", type: "Grizzly"},
    %Bear{id: 7, name: "Rosie", type: "Black", hibernating: true},
    %Bear{id: 8, name: "Roscoe", type: "Panda"},
    %Bear{id: 9, name: "Iceman", type: "Polar", hibernating: true},
    %Bear{id: 10, name: "Kenai", type: "Grizzly"}
  ]
end

As always, feel free to use whatever type of data you like. The actual content isn’t important!

Exercise: Delegate DELETE to a Controller Action

In a previous exercise, you defined a route that handles a DELETE request. Now, change that route to delegate to a new function/action in the BearController.

Solution:

# in handler.ex
 
def route(%{method: "DELETE", path: "/bears/" <> _id} = conv) do
BearController.delete(conv, conv.params)
end
 
# in bear_controller.ex
 
def delete(conv, _params) do
%{ conv | status: 403, resp_body: "Deleting a bear is forbidden!"}
end

Capturing Expressions

In the video, we used the &amp; operator to capture named functions. For example, we captured the String.upcase function:

iex> phrases = ["lions", "tigers", "bears", "oh my"]
 
iex> Enum.map(phrases, &String.upcase(&amp;1))
["LIONS", "TIGERS", "BEARS", "OH MY"]

The &amp; capture operator creates an anonymous function that calls String.upcase. The &amp;1 is a placeholder for the first argument passed to the function. It’s shorthand for:

iex> Enum.map(phrases, fn(x) -> String.upcase(x) end)

You can also capture expressions using the &amp; operator. For example, to triple a list of numbers by calling map with a list and an anonymous “tripler” function:

iex> Enum.map([1, 2, 3], fn(x) -> x * 3 end)
[3, 6, 9]

Here’s the shorthand way using the & capture operator:

iex> Enum.map([1, 2, 3], &(&1 * 3))
[3, 6, 9]

You can also bind the anonymous function to a variable:

iex> triple = &(&1 * 3)
#Function<6.118419387/1 in :erl_eval.expr/5>
 
> Enum.map([1, 2, 3], triple)
[3, 6, 9]

Exercise: Capture Functions

For extra practice, write an anonymous function that adds two numbers and call it.

iex> add = fn(a,b) -> a + b end
 
iex> add.(1, 2)
3
iex> add = &(&1 + &2)
 
iex> add.(3, 5)
8

Then, write a shorthand version using the &amp; capture operator. Next, look up the documentation for String.duplicate and try capturing it in two ways.

Exercise: Implement my_map

In a previous exercise, you used recursion to triple numbers in a list. Now, you can generalize that idea using Enum.map:

iex> nums = [1, 2, 3, 4, 5]
 
iex> Enum.map(nums, &(&1 * 2))
[2, 4, 6, 8, 10]
 
iex> Enum.map(nums, &(&1 * 4))
[4, 8, 12, 16, 20]
 
iex> Enum.map(nums, &(&1 * 5))
[5, 10, 15, 20, 25]

Now, try implementing your own version of map to validate what you’ve learned:

Solution:

defmodule Recurse do
  def my_map([head | tail], fun) do
    [fun.(head) | my_map(tail, fun)]
  end
 
  def my_map([], _fun), do: []
end
 
IO.inspect Recurse.my_map([1, 2, 3, 4, 5], &(&1 * 2))

Exercise: Reduce the Headers

In a previous video, we parsed request headers using recursion. Now, refactor that function using Enum.reduce:

Solution:

def parse_headers(header_lines) do
  Enum.reduce(header_lines, %{}, fn(line, headers_so_far) ->
    [key, value] = String.split(line, ": ")
    Map.put(headers_so_far, key, value)
  end)
end

This reduces the list of headers into a single map of key-value pairs, which simplifies the previous recursive implementation.

Code So Far

The code for this section is in the enum directory found within the video-code directory of the code bundle

Prev Next