The Elixir Enum Module
Elixir Enum Module
To help myself in learning more Elixir, I'm going to continue these Elixir guide/tutorial posts where I try to explain a different piece of Elixir in each post. Today's post will be covering the Enum module and it's usage.
The Enum Module provides a set of algorithms that enumerate over collections according to the Enumerable protocol:
iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
[2,4,6]
Some particular types, like dictionaries, yield a specific format on enumeration. For dicts, the argument is always a {key, value} tuple:
iex> dict = %{a: 1, b: 2}
iex> Enum.map(dict, fn {k, v} -> {k, v * 2} end)
[a: 2, b: 4]
Below are some other functions available in the Enum Module along with a link to it's definition in the Enum Module documentation.
at/2
filter/2
reduce/3
into/2
take/2
The Capture Operator
Let’s first talk about capturing function. Capture means "&" can turn a function into an anonymous function which can be passed as arguments to other function or be bound to a variable.
& can capture two types of functions, a function with given name and arity from a module.
The notation is: &(module_name.function_name/arity)
ex:
speak = &(IO.puts/1)
speak.("hello") # hello
We capture puts function from IO module and bind it with a local name speak.
The capture operator can be a little difficult to wrap your head around, so here are some examples and a helpful image to help grasp this concept:
# Multiple each number by itself
Enum.map [1, 2, 3], fn(num) ->
num * num
end
# Shortened with capture operator:
# Parentheses are required around the capture in this
# case to make it clear where the capture starts and ends.
Enum.map([1, 2, 3], &(&1 * &1))
When you are capturing a named function, you don’t need the parentheses:
# Remove \n chars from the end of each word
Enum.map ["hello\n", "there\n"], fn(word) ->
String.replace(word, "\n", "")
end
# Shortened with capture operator:
Enum.map(["hello\n", "there\n"],
&String.replace(&1, "\n", ""))
Read the documentation on the Capture operator for more details.
Stream
Stream is a lazy version of the Enumerable module. Note that the functions in the Enum module are eager: they always start the enumeration of the given collection. The Stream module allows lazy enumeration of collections and provides infinite streams. It implements most Enum functions, but instead of returning a modified list, it returns a struct like this:
%Stream{
enum: [...], # Enumerable to iterate through
funs: [...] # Anonymous functions to run
}
Since the majority of the functions in Enum enumerate the whole collection and return a list as result, infinite streams need to be carefully used with such functions, as they can potentially run forever. For example:
Enum.each Stream.cycle([1,2,3]), &IO.puts(&1)
Streams are lazy, and only iterate over the list once:
# Iterates over the list twice
list
|> Enum.filter(&is_number/1)
|> Enum.filter(&(&1 * 2 == 4))
# Iterates over the list once
list
|> Stream.filter(&is_number/1)
|> Stream.filter(&(&1 * 2 == 4))
|> Enum.into([])
Use Enum.into/2 or Stream.run/1 to make a stream do work.
list
|> Stream.filter(&is_number/1)
|> Stream.filter(&(&1 * 2 == 4))
|> Enum.into([])
[1, 2, 3]
|> Stream.each(&IO.puts/1)
|> Stream.run
Checkout some more Stream building functions in the documentation:
cycle/1
iterate/2
resource/3
Resources
Enum Documentation
Capture Operator Documentation
Stream Documentation
Nice post! I've never done any research about Elixir so for me this was the first time seeing any code written in it. Good luck on learning the language! I also encourage you to post your programming related things in the Programming Community. We welcome everyone from total beginners to the more advanced developers to share their experiences, projects, etc!
https://twitter.com/nolyoi/status/1308154835188043779