DEV Community

Mackenzie
Mackenzie

Posted on • Originally published at mackenzie.morgan.name

Start of a List

Elixir has a lot of ways to get the first thing in a list. One of the first things you learn from the basic syntax guide on the Elixir website is that hd(foo) gets the first thing in a list, and tl(foo) gets the rest. You also learn [ head | tail ] = foo.

But what happens when it's an empty list?

iex(1)> foo = []
[]
iex(2)> hd(foo)
** (ArgumentError) argument error
    :erlang.hd([])
iex(3)> tl(foo)
** (ArgumentError) argument error
    :erlang.tl([])
Enter fullscreen mode Exit fullscreen mode

Those first bits of syntax you learned will throw errors if given empty lists. That means you need to only use them after confirming the list has stuff in it, such as with a conditional or by pattern-matching.

If you aren't guaranteed the list has anything at all in it (such as after piping through an Enum.filter/2 or Enum.reject/2), you need to go deeper into the standard library.

Three ways I know to get the first item are Enum.at/2, List.first/1, and Enum.take/2. At this point the question is: what kind of output do you need?

If you want to get back the actual item itself (like you'd get from hd/1), two ways to ensure you get nil (instead of an error) when it's empty are to use Enum.at/2 or List.first/1.

iex(1)> foo = []
[]
iex(2)> Enum.at(foo, 0)
nil
Enter fullscreen mode Exit fullscreen mode

or

iex(1)> foo = []
[]
iex(2)> List.first(foo)
nil
Enter fullscreen mode Exit fullscreen mode

If, on the other hand, you need a list (albeit, a potentially empty one) to make later functions in your pipeline happy? In that case, try Enum.take/2

iex(1)> foo = []
[]
iex(2)> Enum.take(foo, 1)
[]
Enter fullscreen mode Exit fullscreen mode

(Enum.take/2 will return a list with length up to the number you specified, as available from the list. Give it a list of length 3 and ask for 5? You'll get all 3 items back.)

Discussion (0)