Counting a List? Think Twice!
2021-11-15 Elixir ListCounting a list to check whether its size is under certain constraints is the most common mistake I can see:
# Don't do:
if length(my_list) > 0, do: something()
As we know, regarding a linked list is noncontiguous in memory, getting the length of a list in Elixir/Erlang works in a linear, or O(n)
, time. So the above code may be slow when my_list
is large. Do you need to measure the accurate distance to somebody in order to know she's more than a block away? You don't be cause there are better ways.
What are the alternatives?
1. Use pattern matching if you can
For example:
To check whether a list is empty:
def empty_list?(list), do: list == []
To check whether a list's length is exactly 1:
def one_elem_list?([_]), do: true
def one_elem_list?(_), do: false
To check whether a list's length is exactly 2:
def two_elem_list?([_, _]), do: true
def two_elem_list?(_), do: false
You may wonder what if the number is dynamic. Well, we can write a function for that:
2. A "list_not_shorter_than?" function
def list_not_shorter_than?([_ | tail], n) when is_integer(n) and n > 0,
do: list_not_shorter_than?(tail, n - 1)
def list_not_shorter_than?([], len) when is_integer(len),
do: len <= 0
def list_not_shorter_than?(_, len) when is_integer(len),
do: true
3. Use the "Enum.count_until/2" function
We can also use Enum.count_until/2
or Enum.count_until/3
which were introduced into Elixir since 1.12.0.
def list_not_shorter_than?(list, n) when is_integer(n) and n >= 0,
do: Enum.count_until(list, n + 1) >= n
It is worth noting that Enum.count_until
is not for lists, but all enumerable data, e.g. maps. In fact, any data with the implementation of Enumerable.count/1
protocol works.
Conclustion
Never count a list to check if it is empty.
Comments