Elixir Comprehensions - The "for" macro

avatar
(Edited)

image.png

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:

  1. Generators
  2. Filters
  3. Collectables (The :into Option)

Enum vs. Stream vs. for

image.png

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



0
0
0.000
11 comments
avatar

Hello again. Thanks for sharing this in the Programming community. While I won't be trying it at the moment, I'd love to see what you come up with!

0
0
0.000
avatar

It is plagiarism. Please check the Hivewatchers source in te comment below (this post was edited later).

0
0
0.000
avatar
(Edited)

Hi there,

I did look at the post history and I understand where you are coming from. The text portion of the post is quite similar to that of the documentation (and could've been lifted - which I wouldn't approve) but I like to err on the safe side and I've written reports about computing concepts (for college) with over 25% "plagerised" text because some concepts can't be explained in a different way. I would like to see this content improve. I weighted it myself off of the fact that he had understood it (so it seems, I can't write elixr) and provided unique examples (yes the first example is the same as the documentation I am aware).

I hope we can come to a reasonable understanding together, if you think I'm talking out of my ass and it is plagerised, that's a fair opinion to have. I'm just talking from my perspective.

To @nolyoi:
I am really glad that you posted in the programming community and showed your understanding of the subject matter by providing unique examples. If the text is lifted, this is something that shouldn't be done in future posts. I sure know writing isn't my forté (and this is probably the most elegant post I've written in the past year 😂) but unique content is important.

Many thanks,
~ CA

0
0
0.000
avatar

Hi,

As you see in post history, this user edited parts after being caught.

"if you think I'm talking out of my ass and it is plagerised"

Not at all :-)

When I looked at plagiarism in this post, I also took into account copypasta/plagiarism found in 2 other posts published by this user. This suggested that it was not a one-off mistake.

0
0
0.000
avatar

Hello again,

I have now taken some time to look at previous posts, and I must admit your argument is pretty convincing. I even found this post about the enum module that seems to co-incide a lot with Nolyoi's Post. The uniswap post isn't even code related and looks like a pretty like-for like copy with some words removed.

I am going to see about getting the OCD vote removed and then I will hide this post within the community.

Many thanks for the help,
~ CA

0
0
0.000
avatar

Source
Plagiarism is the copying & pasting of others' work without giving credit to the original author or artist. Plagiarized posts are considered fraud and violate the intellectual property rights of the original creator.

Fraud is discouraged by the community and may result in the account being Blacklisted.

If you believe this comment is in error, please contact us in #appeals in Discord.

0
0
0.000
avatar

How is it fraud to mix in examples from a documentation? I even linked the documentation in the post several times lol. Good job.

0
0
0.000
avatar

Congratulations @nolyoi! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s) :

You received more than 600 upvotes. Your next target is to reach 700 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @hivebuzz:

Update for regular authors
0
0
0.000
avatar

@hivewatchers @themarkymark

How am I on a blacklist for posting 1 post against your rules? I don't see how I even broke the rules. I used some examples from the Elixir docs as well as some of my own examples that I made. Then added some of my own descriptions of things. AND I linked to the sources of the examples.

0
0
0.000