在 Elixir 中按内部映射值查找键

Find key by inner map value in Elixir

假设我有:

%{apple: %{color: red, id: 1}, pear: %{color: green, id: 2}}

按值循环获取密钥(例如苹果)的最快方法是什么(例如,颜色:红色)

这里可以用

Enum.filter(enumerable, fun)过滤地图。正如 doc 所说,它会过滤可枚举的元素,即 returns 仅过滤那些 fun returns 为真值的元素。我们在地图上使用 filter/2 后得到的是一个关键字列表,如下所示: [a: 1, b: 2] 我们使用 Keyword.keys/1.

从中提取关键字
fruits = %{apple: %{color: "red", id: 1}, pear: %{color: "green", id: 2}}

fruits
|> Enum.filter(&match?({_fruit, %{color: "red"}}, &1))
|> Keyword.keys()

此示例还显示了 match?/2 宏,它允许我们对各种谓词函数使用模式匹配,但因此对这种情况使用理解会更快,因为我们可以过滤和 return 一次传递正确的值。

fruits = %{apple: %{color: "red", id: 1}, pear: %{color: "green", id: 2}}

for {hit, %{color: "red"}} <- fruits, do: hit

Comprehensions 让 filtering/mapping 简单:

iex> red = :red
:red
iex> green = :green
:green
iex> fruits = %{apple: %{color: red, id: 1}, pear: %{color: green, id: 2}}
%{apple: %{color: :red, id: 1}, pear: %{color: :green, id: 2}}
iex> for {fruit, %{color: ^red}} <- fruits, do: fruit
[:apple]

我们可以利用映射的可枚举性(作为 {key, value} 对)到 select 值与某种模式匹配的所有键。

我想添加一个答案以提供一些多样性。理解力很好,但在尝试 pipe/chain 操作时可能会很笨拙。另一种解决方案可能是使用 Stream.filter/2 and Stream.map/2

red = :red

fruits
|> Stream.filter(&match?({_fruit, %{color: ^red}}, &1))
|> Stream.map(&elem(&1, 0))
|> Enum.to_list()

然后您可以对此进行扩展,以创建比您从典型理解中获得的更好的构图如果您想通过不同的 and/or 多个参数进行过滤。例如,您可以编写这样的模块:

defmodule FruitSearch do
  @fruits %{
    apple: %{color: :red, taste: :tart},
    banana: %{color: :yellow, taste: :sweet},
    lemon: %{color: :yellow, taste: :sour}
  }

  @spec query_fruits(%{
          optional(:color) => atom,
          optional(:taste) => atom
        }) :: [atom]
  def query_fruits(params) do
    @fruits
    |> maybe_filter_by_color(params)
    |> maybe_filter_by_taste(params)
    |> Stream.map(&elem(&1, 0))
    |> Enum.to_list()
  end

  defp maybe_filter_by_color(enum, %{color: color}) do
    Stream.filter(enum, &match?({_fruit, %{color: ^color}}, &1))
  end

  defp maybe_filter_by_color(enum, _params) do
    enum
  end

  defp maybe_filter_by_taste(enum, %{taste: taste}) do
    Stream.filter(enum, &match?({_fruit, %{taste: ^taste}}, &1))
  end

  defp maybe_filter_by_taste(enum, _params) do
    enum
  end
end

示例:

iex> FruitSearch.query_fruits(%{color: :yellow})
[:banana, :lemon]

iex> FruitSearch.query_fruits(%{taste: :sour, color: :yellow})
[:lemon]

iex> FruitSearch.query_fruits(%{})
[:lemon, :banana, :apple]

解决方案主要取决于用例。如果您不需要这么复杂的东西,理解是一个很好的解决方案。