Skip to content

Instantly share code, notes, and snippets.

@ybur-yug
Last active October 19, 2016 23:15
Show Gist options
  • Save ybur-yug/ebc5d7fb65ec1656bf8c39b2ea381954 to your computer and use it in GitHub Desktop.
Save ybur-yug/ebc5d7fb65ec1656bf8c39b2ea381954 to your computer and use it in GitHub Desktop.

Collections {#collections .page-title}

===========

List, tuples, keywords, maps and functional combinators.

Table of Contents

Lists

Lists are simple collections of values, they may include multiple types; lists may include non-unique values:

iex> [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]

Elixir implements list as linked lists. This means accessing the list length is an O(n){.highlighter-rouge} operation. For this reason, it is typically faster to prepend than append:

iex> list = [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]
iex> ["π"] ++ list
["π", 3.14, :pie, "Apple"]
iex> list ++ ["Cherry"]
[3.14, :pie, "Apple", "Cherry"]

List Concatenation

List concatenation uses the ++/2{.highlighter-rouge} operator:

iex> [1, 2] ++ [3, 4, 1]
[1, 2, 3, 4, 1]

A side note about the name (++/2{.highlighter-rouge}) format used above: In Elixir (and Erlang, upon which Elixir is built), a function or operator name has two components: the name you give it (here ++{.highlighter-rouge}) and its arity. Arity is a core part of speaking about Elixir (and Erlang) code. It is the number of arguments a given function takes (two in this case). Arity and the given name are combined with a slash. We’ll talk more about this later; this knowledge will help you understand the notation for now.

List Subtraction

Support for subtraction is provided via the --/2{.highlighter-rouge} operator; it’s safe to subtract a missing value:

iex> ["foo", :bar, 42] -- [42, "bar"]
["foo", :bar]

Be mindful of duplicate values. For every element on the right, the first occurrence of it gets removed from the left:

iex> [1,2,2,3,2,3] -- [1,2,3,2]
[2, 3]

Note: It uses strict comparison to match the values.

Head / Tail {#head--tail}

When using lists it is common to work with a list’s head and tail. The head is the list’s first element while the tail is the remaining elements. Elixir provides two helpful methods, hd{.highlighter-rouge} and tl{.highlighter-rouge}, for working with these parts:

iex> hd [3.14, :pie, "Apple"]
3.14
iex> tl [3.14, :pie, "Apple"]
[:pie, "Apple"]

In addition to the aforementioned functions, you can use pattern matching and the cons operator |{.highlighter-rouge} to split a list into head and tail; we’ll learn more about this pattern in later lessons:

iex> [h|t] = [3.14, :pie, "Apple"]
[3.14, :pie, "Apple"]
iex> h
3.14
iex> t
[:pie, "Apple"]

Tuples

Tuples are similar to lists but are stored contiguously in memory. This makes accessing their length fast but modification expensive; the new tuple must be copied entirely to memory. Tuples are defined with curly braces:

iex> {3.14, :pie, "Apple"}
{3.14, :pie, "Apple"}

It is common for tuples to be used as a mechanism to return additional information from functions; the usefulness of this will be more apparent when we get into pattern matching:

iex> File.read("path/to/existing/file")
{:ok, "... contents ..."}
iex> File.read("path/to/unknown/file")
{:error, :enoent}

Keyword lists

Keywords and maps are the associative collections of Elixir. In Elixir, a keyword list is a special list of tuples whose first element is an atom; they share performance with lists:

iex> [foo: "bar", hello: "world"]
[foo: "bar", hello: "world"]
iex> [{:foo, "bar"}, {:hello, "world"}]
[foo: "bar", hello: "world"]

The three characteristics of keyword lists highlight their importance:

  • Keys are atoms.
  • Keys are ordered.
  • Keys are not unique.

For these reasons keyword lists are most commonly used to pass options to functions.

Maps

In Elixir maps are the “go-to” key-value store. Unlike keyword lists they allow keys of any type and are un-ordered. You can define a map with the %{}{.highlighter-rouge} syntax:

iex> map = %{:foo => "bar", "hello" => :world}
%{:foo => "bar", "hello" => :world}
iex> map[:foo]
"bar"
iex> map["hello"]
:world

As of Elixir 1.2 variables are allowed as map keys:

iex> key = "hello"
"hello"
iex> %{key => "world"}
%{"hello" => "world"}

If a duplicate is added to a map, it will replace the former value:

iex> %{:foo => "bar", :foo => "hello world"}
%{foo: "hello world"}

As we can see from the output above, there is a special syntax for maps containing only atom keys:

iex> %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}

iex> %{foo: "bar", hello: "world"} == %{:foo => "bar", :hello => "world"}
true

Another interesting property of maps is that they provide their own syntax for updating and accessing atom keys:

iex> map = %{foo: "bar", hello: "world"}
%{foo: "bar", hello: "world"}
iex> %{map | foo: "baz"}
%{foo: "baz", hello: "world"}
iex> map.hello
"world"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment