Elixir:减少列表以映射计数发生(模式匹配另一个参数上的映射键)

时间:2017-03-24 19:11:26

标签: dictionary count duplicates pattern-matching elixir

我有一个可能包含重复项的列表。我想计算列表中每个项目的实例数。我的计划是:

list
|> Enum.reduce(%{}, fn
  item, %{item => count}=acc -> %{acc | item => count + 1}
  item, acc -> Map.put(acc, item, 1)
end)

但是,无法使用错误illegal use of variable item inside map key match, maps can only match on existing variable by using ^item进行编译。

所以我将第一个模式更改为item, %{^item => count}=acc。此时,错误变为unbound variable ^item

我不知道该怎么做。我知道可以基于另一个模式匹配一​​个参数(如fn a, a -> true中的一个比较函数的头部),但显然不是这种情况。我试着用守卫做这件事,但Map.has_key?/2不能戴卫兵。我在这里发现了许多关于在地图上匹配的问题,但是当匹配的值来自另一个参数时,我没有这样做。

2 个答案:

答案 0 :(得分:15)

修改Map中某个键的值并插入(如果它尚未存在)正是Map.update/4的作用。要计算频率,默认值为1,更新fn只会将值加{1 &(&1 + 1)):

iex(1)> [1, 2, :a, 2, :a, :b, :a] |>
...(1)> Enum.reduce(%{}, fn x, acc -> Map.update(acc, x, 1, &(&1 + 1)) end)
%{1 => 1, 2 => 2, :a => 3, :b => 1}

答案 1 :(得分:0)

嗯,在写这个问题时发现了这一点。想我分享它,但如果其他人有更清洁的解决方案,他们欢迎它:

我发现的最佳解决方案是......在某种程度上内部讨论其中一个论点,以便它受到约束,纯粹出于语法目的。

list
|> Enum.reduce(%{}, fn item, acc ->
    f = fn %{item => count}=acc -> %{acc | item => count + 1}
            acc when is_map(acc) -> Map.put(acc, item, 1)
        end
    f.(acc)
  end)