BinaryWebPark

Announcing Nested Filter For Elixir

August 29, 2017

Announcing Nested Filter: v0.1.5

Last update: 10/24/17

This post is about a package I made called nested_filter, the background behind it, and what I learned from it.

Elixir nested filter

API calls and the motivation behind Nested Filter

This all came about due to another Elixir package I started building called yt_potion. I started using structs with default parameters (usually set to nil). But the issue was that passing nil values meant the YouTube API would return an error response.

So I had to figure out a way to filter out all the nil and other values causing this issue.

The Problem in Code

To give you an idea of the problem, here is a code snippet of the functionality I would like from Map.drop in Elixir:

nested_map = %{a: 1, b: %{c: nil, d: nil}, c: nil}

Map.drop(nested_map, [:c, :d])

# => %{a: 1, b: %{c: nil, d: nil}}

# But you actually wanted:
# => %{a: 1}

The Current API

Right now there are 2 methods – dropbyvalue and dropbykey. The full API is documented in the README of the GitHub page.

To give you an idea, here is an example of using dropbyvalue:

# Remove the nil values from a nested map, preserving empty map values

nested_map = %{a: 1, b: %{m: nil, n: 2}, c: %{p: %{q: nil, r: nil}, s: %{t: 2, u: 3}} }
NestedFilter.drop_by_value(nested_map, [nil])

# => %{a: 1, b: %{n: 2}, c: %{p: %{}, s: %{t: 2, u: 3}} }

Why I Released This As An Elixir Package

My reason for releasing this was two-fold. One was to help solve my problem in calling the YouTube API . The other was to get more familiar with Elixir conventions and learn something new.

What I Learned By Releasing This Package

One thing I learned is how helpful the Elixir community is. I got a great critique from scohen and learned the following from that critique.

Cond is less “idiomatic” than case

Don’t do this:

cond do
  is_map(map) ->
    map
    |> Enum.any?(fn{key, _} -> is_map(map[key]) end)
  true ->
    false
end

Prefer this:

case map do
  %{}  ->
    |> Enum.any?(map, fn {_key, val} -> is_map(val) end)
  _ ->
    false
end

Prefer pattern matching to conditionals

Instead of doing the above conditional matching, use pattern matching via functions.

defp is_nested_map?(map) when is_map(map) do
  Enum.any?(map, fn {key, _} -> is_map(map[key]) end)
end
defp is_nested_map?(_), do: false

Idiomatic Naming – Filter vs Reject

In Elixir, filter means you want to keep items and reject means you want to remove them. So name things in your code accordingly.

Summary

The Elixir community seems really friendly and helpful. Feel free to ask people questions in order to level up.

I learned quite a bit from making and releasing nested_filter. The Elixir community is helpful and I look forward to learning more about Elixir.