diceline-chartmagnifiermouse-upquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Make LiveView template variables readable

If you ever seen this in the wild:

{:ok, assign(socket,
      %{my_variable: @my_variable
        my_other_variable: nil.
        yet_another_variable: nil 
      })}

Remember that applying elixir's pipe operator makes everything better:

def mount(_session, socket) do
    socket =
      socket
      |> assign(:my_variable, @my_variable)
      |> assign(:my_other_variable, nil)
      |> assign(:yet_another_variable, nil)
    {:ok, socket}
end

Lists and Recursion in Elixir - Flatten a list

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 ... 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

Parsing a date string into a date sigil 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
)