diceline-chartmagnifierquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Playing with Lists and Recursion in Elixir - Flatten a list exercise

While reading Dave Thomas's book, Programming Elixir, I stumbled upon an exercise.

You're supposed to write a flatten(list) function that takes a list as a parameter. That list may contain any number of sublists, which themselves may contain sublists, no matter how deep. Your function should return all the elements of these lists in a single flattened list, all the while preserving the order of the elements.

In the beginning, it sounded a bit hard (especially since he mentioned in the book that the exercise it's hard), but after enough playing with the idea, I got to a fairly simple solution, simpler than what I thought it would be and the solutions I've found so far on the internet.

The result should look something like this:

iex> MyList.flatten([ 1, [ 2, 3, [4] ], 5, [[[6]]]])
[1,2,3,4,5,6]

Now the function:

defmodule MyList do
  def flatten([]), do: []
  def flatten([head | tail]) when is_list(head) do
      flatten(flatten(head) ++ tail)
  end
  def flatten([head | tail]), do: [head | flatten(tail)]
end

At first I thought it wouldn't work, but tested it and got surprised.

The flatten function takes care of 3 cases:

  • the given list is empty
  • the head of the list is in itself a list, in which case it should be flattened
  • the head is not a list, in which case we continue with flattening the tail

Formating a date string "yyyy-mm-dd" into a date sigil ~D[yyyy-mm-dd] in Elixir

What I was trying to achieve, was to get a person's date of birth from a date_input and calculate the age of that particular person. The problem was that the input was giving back a string like "yyyy-mm-dd" with the date, while I needed a date sigil of the form ~D[yyyy-mm-dd] to use with the diff/2 function.

IO.inspect(date_of_birth) "1978-06-11"

So I learned that you can use the from_iso8601!/1 function to parse the date from a string to an Elixir Date sigil.

IO.inspect(Date.from_iso8601!(date_of_birth)) ~D[1978-06-11]
def calculate_age(date_of_birth) do
    date_of_birth
        |> Date.from_iso8601!()
        |> calculate_date_diff()
        |> div(365)
end
defp calculate_date_diff(date) do
    Date.diff(Date.utc_today(), date)
end

Why the private calculate_date_diff() function? Because the Date.diff() takes the oldest date as its second parameter, while using the Elixir pipeline adds the previous functions's result as a first parameter to the next function.

Extend Ecto's query API with your own macros

Imagine for a second this is something you're using a lot when programming Elixir and Phoenix:

Repo.one(
    from u in User,
        where: fragment("lower(?)", u.email) == ^email
)

You can write your own Elixir macro to improve your flow.

defmacro lower(arg) do
    quote do: fragment("lower(?)", unquote(arg))
end

Then your query can be rewritten like this:

Repo.one(
    from u in User,
        where: lower(u.email) == ^email
)