Pattern match function against empty map

后端 未结 3 1192
执笔经年
执笔经年 2020-12-16 08:49

I\'m playing around with pattern match and I found out, that it\'s not quite easy to pattern match parameters of a method against an empty map. I thought it would go somethi

相关标签:
3条回答
  • 2020-12-16 09:22

    In addition to all the cool answers provided so far, you may also consider the usage of the unary pin operator that looks like a hat or an upper arrow point. You use it to prefix a variable with it to ensure you pattern match against its value, as stated in the relevant documentation:

    Use the pin operator ^ when you want to pattern match against an existing variable’s value rather than rebinding the variable

    Following is an example:

    defmodule A do
      def determine_map_volume(some_map) do
        an_empty_map = %{}
    
        some_map
        |> case do
        ^an_empty_map -> :empty  # Application of pin operator
        _ -> :not_empty
        end
      end
    end
    

    Which you can verify as follows:

    A.determine_map_volume(%{})
    :empty
    A.determine_map_volume(%{a: 1})
    :not_empty
    

    Which method you intend to use depends on your personal/organizational preference for the readability of your code.

    0 讨论(0)
  • 2020-12-16 09:23

    It works this way by design, but admittedly it can be a bit confusing at first glance. This feature allows you to destructure maps using pattern matching, without having to specify all keys. For example:

    iex> %{b: value} = %{a: 1, b: 2, c: 3}
    %{a: 1, b: 2, c: 3}
    
    iex> value
    2
    

    Consequently, %{} will match any map. If you want to match an empty map in a function, you have to use a guard clause:

    defmodule PatternMatch do
      def modify(map) when map == %{} do
        %{}
      end
    
      def modify(map) do
        # ...
      end
    end
    
    0 讨论(0)
  • 2020-12-16 09:45

    In addition to @PatrickOscity's answer (which I would use for an empty map), you can use a map_size/1 guard to match against maps with a number of keys:

    defmodule PatternMatch do
      def modify(map) when map_size(map) == 0 do
        %{}
      end
    
      def modify(map) when map_size(map) == 1 do
        #something else
      end
    
      def modify(map) do
        # expensive operation
        %{ modified: "map" }
      end
    end
    

    Here is an output from iex using Kernel.match?/2 to show map_size/1 in action:

    iex(6)> Kernel.match?(map when map_size(map) == 1, %{})
    false
    iex(7)> Kernel.match?(map when map_size(map) == 1, %{foo: "bar"})
    true
    
    0 讨论(0)
提交回复
热议问题