# Filtering Atoms with Nil Value

Problem Description:

Given a list of atoms like this `[:a, :b, :c, a: 2]`, I need to add 1 to each atom that doesn’t have a value but keep the atoms that do. So, ideally, the list will end up being `[a: 1, b: 1, c: 1, a: 2]` which will then get put into a map.

What I’ve done so far:

The initial problem was adding atoms ([:pink, :purple, :pink, :red]) which can be accomplished pretty easily with Enum.frequencies() or with way more complexity by doing:

`````` def total(list) do
list
|> Enum.map(fn x -> {x, 1} end)
|> Enum.group_by(fn {key, _value}-> key end, fn {_key, value}-> value end)
|> Enum.map( fn {group, value_list} -> {group, Enum.reduce(value_list, 0, fn value, acc -> acc + value end)} end)
|> Enum.into(%{})
end
``````

But I can’t figure out how to handle the atoms with no value when there are any other values in the list. I’m sure this is a simple when or unless statement, but I cannot seem to get the syntax down to make it handle atoms that have no value.

1 Like

Some things to consider, `[:a, :b, :c, a: 2]` is not just a list of atoms, this is in fact a list with some atoms and a keyword list at the end, the `a:2` being without the tuple notation is just syntax sugar because is the last element in the list.

You can try this:

`[:d, :e, f: 1, :g]` and you will get an error of this kind:

“unexpected expression after keyword list. Keyword lists must always come last in lists and maps …”

So if you want to handle a list that can mix just atoms and the keyword list, you should better work with the tuple representation:

`[:d, :e, {:f, 1}, :g]`

With that in mind you can do something like this:

``````Enum.map(list, fn
{_, _} = kw -> kw
a when is_atom(a) -> {a, 1}
end)
``````
3 Likes

Here are my few cents …

If you are familiar with operators you can write something like `a && b || c`. First let’s describe the order. It’s really simple to check it:

``````iex> quote do
...>   1 && 2 || 3
...> end
{:||, [(…)],
[{:&&, [(…), [1, 2]}, 3]}
``````

Ok, so `&&` operator have a bigger priority and the result of it is simply passed as a first argument to `||` operator.

We can use it to write a simple condition:

``````iex> func = fn term ->
...>  is_nil(term) && "it's nil" || "it's not nill"
...> end

iex> func.(nil)
"it's nil"
iex> func.("not nil")
"it's not nil"
``````

What happen inside the function? We have a 3 simple steps:

1. Firstly `is_nil/1` check is called
2. Secondly `&&` (boolean and operator) evaluates and returns the second expression only if the first one (`is_nil/1` check) evaluates to `true` (i.e. truthy value). Returns the first expression otherwise (here: `false` i.e. falsy value).
3. Finally `||` (boolean or operator) evaluates and returns the second expression only if the first one (result of `&&` operator) does not evaluate to a truthy value (here: `false`). Returns the first expression (here: second expression of `&&` operator) otherwise.

Great! Now let’s use it in `Enum.map/2` call with a `&` (capture operator):

``````iex> list = [:a, :b, :c, a: 2]
[:a, :b, :c, a: 2]
iex> Enum.map(list, &(is_atom(&1) && {&1, 1} || &1))
[a: 1, b: 1, c: 1, a: 2]
``````

In that case we do not need to write 2 pattern matching expressions inside a `Enum.map/2` mapper function, but that’s not everything … Your `total/1` function is not really efficient. You can even use a single `Enum.reduce/3` call!

``````defmodule Example do
def original(list) do
list
|> Enum.map(&((is_atom(&1) && {&1, 1}) || &1))
|> Enum.group_by(fn {key, _value} -> key end, fn {_key, value} -> value end)
|> Enum.map(fn {group, value_list} ->
{group, Enum.reduce(value_list, 0, fn value, acc -> acc + value end)}
end)
|> Enum.into(%{})
end

def new(list) do
Enum.reduce(list, %{}, fn element, acc ->
{key, value} = (is_atom(element) && {element, 1}) || element
Map.update(acc, key, value, &(&1 + value))
end)
end
end

iex> list = [:a, :b, :c, a: 2]
[:a, :b, :c, a: 2]
iex> Example.original(list) == Example.new(list)
true
``````

That’s just one iteration over one list with only 4 lines of code! What happens here?

Let’s give it a check:

1. First of all we are `reducing` an existing `list` over an empty `map`. Since `map` is our desired result we can simply use the return value of `Enum.reduce/3` function without any extra transformations.
2. In second line of function body we determine both key and a default `or` extra value we want to add (depending if said `key` is already in `map` or not).
3. Finally we call a `Map.update/4` function which puts a default `value` if `key` does not exists or adds extra `value` if `key` already exists into our `acc` (a `map` accumulator).

Thanks, I have updated my `Helpful resources` section