Elixir by Example

Enum

Edit any cell and run it in your browser Download source

Enum is the toolbox for anything enumerable: lists, maps, ranges, MapSets and more. Its functions are eager: each one walks the whole collection and returns a new list (or a single value).

numbers = [5, 3, 8, 1, 9, 2]

IO.inspect(Enum.map(numbers, fn n -> n * 10 end), label: "map")
IO.inspect(Enum.filter(numbers, &(&1 > 4)), label: "filter")
IO.inspect(Enum.reject(numbers, &(&1 > 4)), label: "reject")
map: [50, 30, 80, 10, 90, 20]
filter: [5, 8, 9]
reject: [3, 1, 2]

&(&1 > 4) is the capture shorthand for fn x -> x > 4 end. &String.upcase/1 captures a named function.

IO.inspect(Enum.map(["a", "b"], &String.upcase/1), label: "capture")
capture: ["A", "B"]

Reducing a collection to one value.

IO.inspect(Enum.sum(numbers), label: "sum")
IO.inspect(Enum.max(numbers), label: "max")
IO.inspect(Enum.reduce(numbers, 0, fn n, acc -> acc + n * n end), label: "sum of squares")
sum: 28
max: 9
sum of squares: 184

Sorting, with a key function or a direction.

words = ["pear", "fig", "banana", "kiwi"]
IO.inspect(Enum.sort(words), label: "sort")
IO.inspect(Enum.sort_by(words, &String.length/1), label: "by length")
IO.inspect(Enum.sort(numbers, :desc), label: "desc")
sort: ["banana", "fig", "kiwi", "pear"]
by length: ["fig", "pear", "kiwi", "banana"]
desc: [9, 8, 5, 3, 2, 1]

Grouping and counting are one call each.

IO.inspect(Enum.group_by(words, &String.length/1), label: "group_by")
IO.inspect(Enum.frequencies(~w(a b a c a b)), label: "frequencies")
group_by: %{3 => ["fig"], 4 => ["pear", "kiwi"], 6 => ["banana"]}
frequencies: %{"a" => 3, "b" => 2, "c" => 1}

Maps enumerate as {key, value} tuples, and Map.new/2 or Enum.into/2 turns them back into a map.

prices = %{apple: 3, pear: 5}
doubled = Map.new(prices, fn {fruit, price} -> {fruit, price * 2} end)
IO.inspect(doubled, label: "doubled")
doubled: %{apple: 6, pear: 10}

Ranges are enumerable too, and Enum.with_index/1, Enum.zip/2 and Enum.chunk_every/2 cover most "loop with a counter" needs.

IO.inspect(Enum.to_list(1..5), label: "range")
IO.inspect(Enum.with_index(["a", "b", "c"]), label: "with_index")
IO.inspect(Enum.zip([1, 2, 3], [:x, :y, :z]), label: "zip")
IO.inspect(Enum.chunk_every(1..7, 3), label: "chunks")
range: [1, 2, 3, 4, 5]
with_index: [{"a", 0}, {"b", 1}, {"c", 2}]
zip: [{1, :x}, {2, :y}, {3, :z}]
chunks: [[1, 2, 3], [4, 5, 6], ~c"\a"]

Enum.find/2 and Enum.any?/2 stop at the first match instead of walking everything.

IO.inspect(Enum.find(numbers, &(&1 > 7)), label: "find")
IO.inspect(Enum.any?(numbers, &(&1 < 0)), label: "any negative?")
find: 8
any negative?: false

Pitfall: each Enum call builds a full intermediate list. Chaining map |> filter |> take(5) over a million items does all the work before take/2 runs. For large or infinite inputs, use Stream and finish with one Enum call.

Pitfall: the last chunk above printed as ~c"\a". That is [7]: a list of integers that are all printable codepoints is shown as a charlist. The data is fine; only the display differs. Pass charlists: :as_lists to IO.inspect/2 to see plain numbers:

IO.inspect([7, 8, 9], charlists: :as_lists)
[7, 8, 9]

Pitfall: Enum.map/2 on a map returns a list of tuples, not a map. Use Map.new/2 (as above) when you want a map back.