Elixir Cheatsheet

Collections and Enum

Use this Elixir reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Lists

list = [1, 2, 3]
[0 | list]       # [0, 1, 2, 3] — prepend, O(1)
list ++ [4, 5]   # append, O(length(left))
list -- [2]      # remove first matching 2
hd(list)         # 1
tl(list)         # [2, 3]
length(list)     # 3 — O(n)
Enum.at(list, 1) # 2 — O(n), lists are not arrays
List.first(list); List.last(list)
List.flatten([1, [2, [3]]])   # [1, 2, 3]

Lists are linked lists. Prepending is cheap; indexing and appending walk the list.

Tuples

point = {:point, 3, 4}
elem(point, 1)         # 3 — O(1) access
put_elem(point, 2, 5)  # {:point, 3, 5}
tuple_size(point)      # 3

Tuples are fixed-size and used for tagged results: {:ok, value} and {:error, reason}.

Keyword Lists

opts = [limit: 10, sort: :asc]        # sugar for [{:limit, 10}, {:sort, :asc}]
opts[:limit]                          # 10; nil if missing
Keyword.get(opts, :sort, :desc)
Keyword.put(opts, :limit, 20)
Keyword.fetch!(opts, :limit)          # raises if missing
Keyword.validate!(opts, limit: 10, sort: :asc)  # reject unknown keys
Keyword.merge(defaults, overrides)
Keyword.has_key?(opts, :sort)

Keyword lists are lists of {atom, value} tuples: ordered, allow duplicate keys, and pattern-match positionally (so prefer Keyword.get/3 over matching). They are the idiomatic options type; use maps for general key-value data.

Ranges

1..5 |> Enum.to_list()       # [1, 2, 3, 4, 5]
5..1//-1 |> Enum.to_list()   # [5, 4, 3, 2, 1]
1..10//2 |> Enum.to_list()   # [1, 3, 5, 7, 9] — step syntax
n in 1..100                  # membership check

Enum Essentials

Enum.map(nums, &(&1 * 2))
Enum.filter(nums, &(rem(&1, 2) == 0))
Enum.reject(nums, &(&1 < 0))
Enum.reduce(nums, 0, fn x, acc -> x + acc end)
Enum.sum(nums); Enum.product(nums)
Enum.count(nums); Enum.count(nums, &(&1 > 0))
Enum.min(nums); Enum.max(nums); Enum.min_max(nums)
Enum.any?(nums, &(&1 > 10)); Enum.all?(nums, &(&1 >= 0))
Enum.find(nums, &(&1 > 10))          # first match or nil
Enum.member?(nums, 3)                # same as 3 in nums
Enum.sort(nums); Enum.sort(nums, :desc)
Enum.uniq(nums)
Enum.reverse(nums)
Enum.take(nums, 3); Enum.drop(nums, 3)
Enum.take_while(nums, &(&1 < 5)); Enum.drop_while(nums, &(&1 < 5))
Enum.join(words, ", ")

Enum for Shaping Data

Enum.sort_by(users, & &1.age)                    # sort by a key
Enum.sort_by(users, & &1.age, :desc)
Enum.max_by(users, & &1.score)
Enum.group_by(words, &String.first/1)            # %{"a" => [...], "b" => [...]}
Enum.frequencies(["a", "b", "a"])                # %{"a" => 2, "b" => 1}
Enum.with_index(["a", "b"])                      # [{"a", 0}, {"b", 1}]
Enum.zip([1, 2], [:a, :b])                       # [{1, :a}, {2, :b}]
Enum.unzip([{1, :a}, {2, :b}])                   # {[1, 2], [:a, :b]}
Enum.chunk_every([1, 2, 3, 4, 5], 2)             # [[1, 2], [3, 4], [5]]
Enum.chunk_every([1, 2, 3], 2, 1, :discard)      # sliding pairs [[1,2],[2,3]]
Enum.flat_map(users, & &1.tags)                  # map + flatten one level
Enum.dedup([1, 1, 2, 2, 1])                      # [1, 2, 1] — consecutive only
Enum.split_with(nums, &(&1 >= 0))                # {matching, rest}
Enum.into([{:a, 1}, {:b, 2}], %{})               # %{a: 1, b: 2}
Map.new(users, fn u -> {u.id, u} end)            # index a list by key
Enum.map_join(1..3, "-", &to_string/1)           # "1-2-3"
Enum.each(items, &IO.puts/1)                     # side effects, returns :ok
Enum.random(1..6); Enum.shuffle(list); Enum.take_random(list, 2)

MapSet

set = MapSet.new([1, 2, 3])
MapSet.put(set, 4)
MapSet.delete(set, 1)
MapSet.member?(set, 2)              # true — O(log n)
MapSet.union(a, b); MapSet.intersection(a, b); MapSet.difference(a, b)
MapSet.subset?(a, b)
MapSet.new([1, 2]) |> Enum.map(&(&1 * 10))   # sets are enumerable

Use MapSet for uniqueness and fast membership; Enum.uniq/1 for one-off dedup of a list.

Pipelines

[1, 2, 3, 4, 5]
|> Enum.filter(&(rem(&1, 2) == 1))
|> Enum.map(&(&1 * &1))
|> Enum.sum()

The value on the left becomes the first argument of the call on the right.

Comprehensions

for x <- [1, 2, 3], do: x * x
for x <- 1..10, rem(x, 2) == 0, do: x                 # filter clause
for x <- 1..3, y <- 1..3, x < y, do: {x, y}           # nested generators
for {name, score} <- scores, score >= 90, do: name
for word <- words, into: %{}, do: {word, String.length(word)}
for <<b <- "abc">>, do: b + 1                         # binary generator
for x <- 1..5, reduce: 0 do acc -> acc + x end        # reduce: option

Streams

1..1_000_000
|> Stream.map(&(&1 * 2))
|> Stream.filter(&(rem(&1, 3) == 0))
|> Enum.take(5)

File.stream!("big.log") |> Stream.map(&String.trim/1) |> Enum.count()
Stream.iterate(1, &(&1 * 2)) |> Enum.take(8)   # infinite sequences
Stream.cycle([:a, :b]) |> Enum.take(5)

Stream is lazy: nothing runs until an Enum function consumes it. Use streams for large or infinite input to avoid building intermediate lists.