Terence Chow
Terence Chow

Reputation: 11173

Is there an easy way to || nil pattern match in elixir?

I have an enum that I am using group_by with.

Enum.group_by(my_list, fn x -> x.param end)

The result is that it groups it by the param value. I would like to pattern match on that quickly so I have:

%{"a" => a, "b" => b, "c" => c } = Enum.group_by(my_list, fn x -> x.param end)

But this runs into issues when list is missing either "a", "b", or "c" as params.

Notice that I can set it to a variable and it will result in nil if I access it and it doesnt exist:

grouped = Enum.group_by(my_list, fn x -> x.param end)
grouped["a"] # this will show as nil or the result if it exists

But for readability, I wanted to just pattern match and use the variables. Is this something that is unsupported in Elixir?

Upvotes: 1

Views: 3317

Answers (1)

raacer
raacer

Reputation: 5481

Pattern matching is for self-checking assertive code, not just for getting values quickly. Your code checks if there are all specified keys in the map. If you don't need such a check then don't use pattern matching.

For example, let's first assign the result to a variable (which does not vary in Elixir, actually).

groups = Enum.group_by(my_list, fn x -> x.param end)

Then let's deсide what we want to do with this. Do we want to process the groups? Let's do it:

Enum.map(groups, fn({name, list}) -> process_group(name, list) end)

Do we need to process the missing groups? No problem:

missing_groups = Enum.filter(["a", "b", "c"], &(!Map.has_key?(groups, &1)))
Enum.map(missing_groups, fn(name) -> process_missing_group(name) end)

Want to filter by keys? That's not a problem too:

# First variant:
filtered_groups = Enum.into(Enum.filter(groups, fn({k, v}) -> k in ["a", "b", "c"] end), %{})
# Second variant: 
filtered_groups = Map.drop(groups, ["d", "e", "f", "g", "h"]) 

Do you really want to add missing keys? Ok, if so then let's do it:

all_groups = Enum.into(["a", "b", "c"], %{}, &({&1, Map.get(groups, &1)}))
process_all_groups(all_groups)

As you can see, pattern matching for map is not really needed for any of this action. Actually, you don't need the variables at all, they are just for readability.

Now you can say "ok, ok, but I want to do different actions for different groups". This is where you can also use the pattern matching feature if needed.

def process_group(name, nil), do: action_empty(name)
def process_group("a", list), do: action_a(list)
def process_group("b", list), do: action_b(list)
def process_group(name, [x]), do: single_lement_action(name, x)
def process_group(name, list), do: another_action(name, list)

Does this look like what you need?

Upvotes: 1

Related Questions