Apr 04, 2016
Table of contents:
Last week we looked at using Tuples in Elixir. Tuples are a special type that hold many related values as a single whole. This is in contrast to the List type that holds individual items as a list of separate values.
Whilst on the surface these two types can seem very similar, they are actually very different.
In today’s tutorial we will be looking at the properties and characteristics of the List type in Elixir.
Before we get into the details of Lists, first we will take a look at the basics of working with them. A List is defined using square brackets around a list of values of any type:
[:hello, "world", 123]
You can get the length of a List by using the length
function:
length([1, 2, 3])
However, if you try to use the length
function on a Tuple you will get an error:
length({:hello, :world})
# ** (ArgumentError) argument error
Instead you can use the tuple_size
function:
tuple_size({:hello, :world})
This is another case of Elixir pointing you in the right direction. Elixir will use size
when the operation to count the number of elements is constant time, and length
when it’s linear.
As we saw in Working with Strings in Elixir, sometimes you will create a list, but you will be returned a Character List:
[65, 66, 67]
# 'ABC'
This is because these values are printable ASCII numbers and so Elixir will print them to the screen.
The Elixir Standard Library has a List Module that provides a number of useful functions for working with Lists. You don’t need to memorise all of these functions, but here are some of the highlights.
Getting the first or last item of a list:
list = [1, 2, 3, 4]
List.first(list)
# 1
List.last().(list)
# 4
Flatten a list of nested lists:
list = [1, [2, 3], [[4, 5, 6]]]
List.flatten(list)
# [1, 2, 3, 4, 5, 6]
Lists can also work with Enum Module. The Enum Module provides a set of algorithms that enumerate over enumerables according to the Enumerable
protocol.
Again, you don’t need to memorise these functions, you just need to be aware that they exist.
Here are a couple of examples from the Enum Module.
The map
function will iterate through a list and apply the given function to each value:
Enum.map([1, 2, 3], fn x -> x * 2 end)
# [2, 4, 6]
The reduce
function will iterate through a list and pass each value and an accumulator to a given function. The accumulator will then be returned:
Enum.reduce([1, 2, 3, 4], fn x, acc -> x * acc end)
# 24
The filter
function will iterate through a list and return only the values as a list that return a truthy value for the given function:
Enum.filter([1, 2, 3], fn x -> rem(x, 2) == 0 end)
# [2]
One of the most common things you will do with Lists in Elixir is using the head and the tail. The head of a List is the first element, and the tail is the remaining elements as a List. For example, if we have the following List:
list = ["a", "b", "c"]
We would get the head of the List with the hd
function:
hd(list)
# "a"
And we could the the tail of the List with the tl
function:
tl(list)
# ["b", "c"]
Processing a List as a head and tail is used extensively in recursive functions. We will be looking at this in more depth in a future tutorial.
If you’ve worked with a “list” type in another programming language you will probably feel right at home with the Elixir List type.
As you probably already know, lists are extremely useful and so you find yourself using them a lot. In particular, Elixir makes good use of recursive functions, something that we will explore in the coming weeks.