lapinkoira
lapinkoira

Reputation: 9008

Check datatype before pattern matching in Elxir

Is it possible to make multiple case matching like in this example? I want to merge two maps, maps, can be like this:

map1 = %{"test" => [list]}
map2 = %{"test" => [list2]}

I can merge them like this if I know both v1 and v2 are lists:

Map.merge(map1, map2, fn _k, v1, v2 ->
  do something with v1 and v2 knowing they are list
  like Enum.concat(v1, v2)
end)

But it's possible to check v1 and v2 in a case clause?

Map.merge(map1, map2, fn _k, v1, v2 ->
  case v1, v2 do
    [v1] [v2] -> they are lists, do something with them
    _ -> they are other thing
  end
end)

Upvotes: 2

Views: 1391

Answers (2)

Chris Meyer
Chris Meyer

Reputation: 1631

Your syntax is pretty close. Try giving your lambda multiple signatures, which syntactically looks very similar to a case:

defmodule TestMerge do
  def merge(map1, map2) do
    Map.merge(map1, map2, fn
      _k, l1, l2 when is_list(l1) and is_list(l2) -> 
        #they are lists, do something with them
        :lists

      _, _, _ ->
        #they are other thing
        :not_lists
    end)
  end
end

To test, we call it with all four possibilities (the keys indicate which combination, e.g. both maps have a list after the :both key, the first map has a list after the :first key, but not the second map, etc.):

iex> TestMerge.merge(%{first: [], last: 1, both: [], neither: 1},
...> %{first: 2, last: [], both: [], neither: 1})
%{both: :lists, first: :not_lists, last: :not_lists, neither: :not_lists}

Upvotes: 2

guitarman
guitarman

Reputation: 3320

You could check the condition wether map1 and map2 are lists before you would do the merge:

result = 
  cond do
    is_list(map1) && is_list(map2) -> Map.merge(...)
    true -> :they_are_other_thing
  end

If one of map1 and map2 is not a list the true condition will be used and result is :they_are_other_thing in this example. Here are some cond examples.

Upvotes: 1

Related Questions