【问题标题】:Idiomatic Elixir to filter a list of maps by specific not null key values?惯用的 Elixir 通过特定的非空键值过滤地图列表?
【发布时间】:2017-09-15 16:14:08
【问题描述】:

我是 elixir 的新手,正在尝试找出重写此 ruby​​ 方法的正确方法:

def filter_events(events)
  events.select { |event| event[:id].present? && event[:vhost].present? }
end

在灵丹妙药中。这是我目前所拥有的:

def filter_events(events) do
  Enum.filter(events, &(Map.has_key?(&1, :id) && Map.has_key?(&1, :vhost)))
end

有没有更好/更惯用的方法来做到这一点?

【问题讨论】:

  • 如果您包含了 ruby​​ 方法的实际作用,那就太好了。另外,你能解释一下为什么你不认为这是惯用的吗?
  • 我想我正在寻求澄清这是否是惯用的,或者是否有更清洁的方法来做到这一点。我最初考虑使用模式匹配,但我找不到匹配地图值“非空”的方法。 ruby 方法接受一个哈希数组,然后返回一个数组,该数组仅包含具有 idvhost 两个键值的哈希值。

标签: elixir


【解决方案1】:

你的例子很短,没关系;它是可读的。但是,大多数示例,包括 the ones in the docs,都使用内联/匿名函数:

Enum.filter(events, fn(event) ->
  Map.has_key?(event, :vhost) and Map.has_key?(event, :id)
end)

如果您正在寻找更聪明的东西,您可以使用Kernel.match?() 的模式匹配。

Enum.filter(events, &match?(%{id: _, vhost: _}, &1))

或使用内联函数:

Enum.filter(events, fn(event) -> match?(%{id: _, vhost: _}, event) end)

由于您正在尝试检查键是否存在并且它们的值是否不为空,因此以下应该有效:

Enum.filter(events, fn(event) ->
  !is_nil(event[:vhost]) and !is_nil(event[:id])
end)

【讨论】:

  • 这很好,感谢您快速详细的回复。有没有办法根据键本身的值进行匹配,检查它是否存在并且不为零?或者我会做类似的事情:Enum.filter(events, fn(event) -> event[:vhost] and event[:id] end)
  • 是的,这样的事情是合理的,而且读起来很好。
  • 太好了,非常感谢您的帮助!作为旁注,因为它不允许我编辑上面的评论,正确的代码实际上是:Enum.filter(events, fn(event) -> !is_nil(event[:vhost]) and !is_nil(event[:id]) end)
  • 在我的 Elixir 1.6.0 开发者Enum.filter(events, &match?({:id, :vhost, _}, &1)) 上为[%{id: 42, vhost: 42}] 返回一个空数组。它应该写成:Enum.filter(events, &match?(%{id: _, vhost: _}, &1)) 工作(它将匹配%{id: nil, vhost: nil},这与您的上一个示例不同。最后一个示例,唯一执行所要求的示例,恕我直言,远非惯用的 Elixir .
  • @mudasobwa 你说得对,已经晚了。我会再次更新。
【解决方案2】:

为了多样性,我将把这个答案放在这里。据我了解,最惯用的方法是在过滤函数中进行模式匹配。它可能看起来更长,但更灵活。当您的地图可能包含所需键的合法 nil 值时(可能不是您的情况,但仍然如此),它可以更好地处理。

def filter_events(events) do
  Enum.filter(events, fn
    %{id: nil} -> false        # or true if it might be nil
    %{vhost: nil} -> false     # or true if it might be nil
    %{id: _, vhost: _} -> true
    _ -> false
  end)  
end

nil 值无效并应被视为缺少值时,就像您的情况一样,它可能会简化为:

def filter_events(events) do
  Enum.filter(events, fn
    %{id: id, vhost: vhost} when not is_nil(id)
                             and not is_nil(vhost) -> true
    _ -> false
  end)  
end

另一种方法是使用 Kernel.for/1 理解:

def filter_events(events) do
  for %{id: id, vhost: vhost} = e when
        not is_nil(id) and
        not is_nil(vhost) <- events, do: e
end

或者,没有模式匹配(不太惯用):

def filter_events(events) do
  for e <- events, !is_nil(e[:id]), !is_nil(e[:vhost]), do: e
end

或者,如果您不期望 nil 值,只需:

def filter_events(events) do
  for %{id: _, vhost: _} = e <- events, do: e
end

【讨论】:

    猜你喜欢
    • 2017-07-02
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 2015-03-08
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    • 2020-10-10
    相关资源
    最近更新 更多