通过传递给当前函数的字段的子集匹配结构

Matching a struct by a subset of its fields passed into the current function

我正在构建过滤 API。简化事情,我有一些类似于接受结构列表和过滤器映射的函数的东西。我的问题是 Enum.filter&match? 的配对在传入函数参数时不起作用:

def filter_structs(structs, filters) do
  # this always returns []
  structs |> Enum.filter(&match?(^filters, &1))
end

但如果我对过滤器进行硬编码,一切正常:

def filter_structs(structs, _filters) do
  structs |> Enum.filter(&match?(%{some_field: true}, &1))
end

我有这个解决方法,但它不是很好...没有更好的解决方案吗?

def filter_structs(structs, filters) do
  structs
  |> Enum.filter(fn s -> 
    Map.equal?(filters, s |> Map.take(filters |> Map.keys))
  end)
end

映射可以在变量 key 上使用 %{^key => _} 和变量键值对使用 %{^key => ^value}.

进行模式匹配

使用 match?(^filters, map) 匹配整个地图将仅 return true 如果 map === filters,不包含它。

以下实现利用了模式匹配,可能更清楚其意图:

  def filter_structs(structs, filters) do
    Enum.filter(structs, fn struct ->
      Enum.all?(filters, fn {field, value} ->
        match?(%{^field => ^value}, struct)
      end)
    end)
  end

+1 到 sabiwara 的回答。

我觉得你的版本也不错。虽然我可能会在没有管道的情况下编写它:

def filter_structs(structs, filters) do
  keys = Map.keys(filters)
  Enum.filter(structs, &(filters === Map.take(&1, keys)))
end