nolyoi avatar

The Elixir Enum Module

nolyoi

Published: 19 Sept 2020 › Updated: 19 Sept 2020The Elixir Enum Module

The Elixir Enum Module

image.png

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:

image.png

# 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

https://nolanm.dev/posts/16-the-elixir-enum-module

Leave The Elixir Enum Module to:

Written by

Read more #stemsocial posts


Best Posts From nolyoi

We have not curated any of nolyoi's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.

More Posts From nolyoi