squares = for n <- 1..5, do: n * n
IO.inspect(squares, label: "squares")
squares: [1, 4, 9, 16, 25]
A comprehension (for) maps, filters and combines
enumerables in one expression. It returns a list by
default.
squares = for n <- 1..5, do: n * n
IO.inspect(squares, label: "squares")
squares: [1, 4, 9, 16, 25]
Extra clauses after the generator are filters.
evens = for n <- 1..10, rem(n, 2) == 0, do: n
IO.inspect(evens, label: "evens")
evens: [2, 4, 6, 8, 10]
Several generators give every combination, like nested loops.
pairs = for x <- [:a, :b], y <- [1, 2], do: {x, y}
IO.inspect(pairs, label: "pairs")
pairs: [a: 1, a: 2, b: 1, b: 2]
The left side of <- is a pattern. Elements that
don't match are skipped silently, which makes
comprehensions handy for picking out results.
results = [{:ok, 1}, {:error, :timeout}, {:ok, 3}]
oks = for {:ok, value} <- results, do: value
IO.inspect(oks, label: "oks")
oks: [1, 3]
Maps enumerate as {key, value} tuples.
stock = %{apple: 0, pear: 4, fig: 2}
in_stock = for {fruit, n} <- stock, n > 0, do: fruit
IO.inspect(Enum.sort(in_stock), label: "in stock")
in stock: [:fig, :pear]
into: collects into something other than a list,
such as a map or a string. Maps have no order, and
how they print can differ between runtimes (this
very example printed b first in the browser), so
read keys explicitly instead of relying on output.
upcased = for {k, v} <- %{a: "x", b: "y"}, into: %{}, do: {k, String.upcase(v)}
IO.inspect(upcased.a, label: "into map, :a")
IO.inspect(Enum.sort(upcased), label: "sorted pairs")
into map, :a: "X"
sorted pairs: [a: "X", b: "Y"]
Bitstring generators walk binaries byte by byte.
bytes = for <<b <- "hi!">>, do: b
IO.inspect(bytes, label: "bytes")
bytes: ~c"hi!"
uniq: true drops duplicates, and reduce: turns a
comprehension into a fold with an accumulator.
IO.inspect(for(x <- [1, 1, 2, 3, 3], uniq: true, do: x * 10), label: "uniq")
total =
for n <- 1..4, reduce: 0 do
acc -> acc + n
end
IO.inspect(total, label: "reduce")
uniq: [10, 20, 30]
reduce: 10
Pitfall: a comprehension doesn't update outer variables. Writing
count = 0and thenfor x <- list do count = count + 1 endleavescountat 0 (the compiler warns that the inner variable is unused). Usereduce:orEnum.reduce/3to carry state.
Pitfall: the silent skip on pattern mismatch can hide bugs:
for {:ok, v} <- resultsdrops errors without a trace. Match errors explicitly when you need to know about them.