Elixir Comprehensions - The "for" macro
In this post we will be going over comprehensions in Elixir. A “Comprehension” is another word for Elixir’s for macro. It can be used to iterate through an enumerable, like Enum or Stream:
for element <- Enumerable do
element
end
In Elixir, it is common to loop over an Enumerable. Often times we would want to filter out some results and map the values into another list.
Comprehensions are what allow us to achieve this goal.
Let's look at this example where we map a list of integers into their squared values:
for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]
The "for" macro consists of three parts:
- Generators
- Filters
- Collectables (The :into Option)
Enum vs. Stream vs. for
Generators
Generators are written like this:
element <- Enumerable
In the expression below, n <- [1, 2, 3, 4] is the generator. It is literally generating values to be used in the comprehension. Any enumerable can be passed on the right-hand side of the generator expression:
for n <- 1..4, do: n * n
[1, 4, 9, 16]
You can use multiple generators in a single for comprehension. Here is an example of this:
suits = [:hearts, :diamonds, :clubs, :spades]
faces = [2, 3, 4, 5, 6, 7, 8, 9, 10,
:jack, :queen, :king, :ace]
for suit <- suits,
face <- faces,
do: {suit, face}
Generators also support pattern matching on their left-hand side. Non-matching patterns get ignored.
Imagine that instead of a range, we have a keyword list where the key is the atom :good or :bad and we only want to compute the square of the :good values:
values = [good: 1, good: 2, bad: 3, good: 4]
for {:good, n} <- values, do: n * n
[1, 4, 16]
Filters
Alternatively to pattern matching, filters can be used to select some particular elements. Filter expressions are written after generators like this:
for element <- Enumerable, filter do
element
end
For example, we can select the multiples of 3 and discard the rest:
multiple_of_3? = fn(n) -> rem(n, 3) == 0 end
for n <- 0..5, multiple_of_3?.(n), do: n * n
[0, 9]
Comprehensions discard all elements for which the filter expression returns false or nil. All other values are selected.
Like generators, you can also use multiple filters:
for {suit, face} <- deck,
suit == :spades,
is_number(face),
face > 5,
do: {suit, face}
Comprehensions generally provide us with a much more concise representation than using the equivalent functions from the Enum and Stream modules.
:into
In the examples above, all the comprehensions returned lists as their result. But, the result of a comprehension can be inserted into different data structures by passing the :into option to the comprehension.
Return something other than a list with the :into option:
for {key, val} <- %{name: "Daniel", dob: 1991, email: "..."},
key in [:name, :email],
into: %{},
do: {key, val}
The above use case of :into is transforming values in a map, without touching the keys.
Let’s make another example below using streams. Fire up your IEx shell and insert the code below into it.
Since the IO module provides streams, an echo terminal that echoes back the upcased version of whatever is typed can be implemented using comprehensions:
stream = IO.stream(:stdio, :line)
for line <- stream, into: stream do
String.upcase(line) <> "\n"
end
Now type any string into the terminal and you will see that the same value will be printed in upper-case.
uniq: true can also be given to comprehensions to guarantee the results are only added to the collection if they were not returned before. For example:
for x <- [1, 1, 2, 3], uniq: true, do: x * 2
[2, 4, 6]
Note: The targets must support the Collectable protocol.
Variable Scoping
All variables used in for are locally scoped:
name = "Nolan"
for name <- names do
String.upcase(name)
end
name # => "NOLAN"
Documentations
Leave Elixir Comprehensions - The "for" macro to:
Read more #programming 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
- I'm back! And what I've been up to (getting married, working on a skincare app).
- ## I am accumulating! With prices headed down, it's always the best ti ...
- Tezos weekly chart looks amazing, and other Tezos tokens! Don't sleep on them!
- End of season rewards + some quest rewards
- ShapeShift FOX airdrop $150-$7000 worth of FOX!
- How I've made hundreds in Bitcoin doing almost nothing with these apps
- 2 months of DeFi on Tezos
- Splinterlands haul: A LEGENDARY Robo Dragon Knight!
- Plenty - The new DeFi yield farm on Tezos (XTZ)!
- Earning Bitcoin (BTC) from purchases you already make!
- Some photos from my trip to Florida
- VOTE $BANANO! $BAN https://twitter.com/KuCoinItalia/status/1391850381 ...
- My experience building with Flutter and finally coming close to app completion.
- My Top 5 Altcoin HODLs
- Finally got around to farming some CUB!
- File structure visualization of a Flutter mobile app I'm working on. ...
- Why I'm buying up Bitcoin Cash and Monero.
- Now if only $OXEN would pick itself up and get in the top 200. Another ...
- Been stacking up KCS for awhile now because you get almost 13% APR in ...
- How I'm funding my open-source project, and supporting open source software too! If you have any change to spare, doate and get HIVE on tio!