左外连接与 Elixir 的理解

Left outer join with Elixir's for comprehension

我一直在研究我可以用 EnumStreamfor 理解做什么,我正在努力重现左外连接行为。我可以用 Enum.reduce 实现左外连接函数,但如果有某种方法可以用 for 实现,我宁愿使用它。

我知道 python 支持它,我已经看到它 here and here,我想 Elixir 的理解受到了 python 的启发。我们可以在 Elixir 中实现吗?

假设我有两个来自外部 API 或某些 json/xml 文件的列表:

categories = [%{id: 1, name: "beverages"}, %{id: 2, name: "vegetables"}]
products = [%{name: "ice tea", category: 1}, %{name: "sake", category: 1}]

我想离开外部加入他们得到类似的东西:

cat_product == [
  %{category: "beverages", product: "ice tea"},
  %{category: "beverages", product: "sake"},
  %{category: "vegetables", product: "(No product)"}
]

还有类似的东西:

cat_products == [
  %{name: "beverages", products: [list of products]}
  %{name: "vegetables", products: []}
]

您的第一个示例无法在外部用 for 循环优雅地编写,因为左侧列表中的记录可以连接到右侧列表中的多个记录。然而,for 推导式对原始集合中的每个元素至多产生一个结果。在这种情况下,使用 Enum.flat_map:

会更合适
Enum.flat_map categories, fn(c) ->
  case Enum.filter(products, fn(p) -> p.category == c.id end) do
    [] ->
      [%{name: c.name, product: nil}]
    prods ->
      for p <- prods, do: %{name: c.name, product: p.name}
  end
end

你的第二个例子可以很容易地用 for 理解来实现,因为在原始集合中的每个元素的结果集中总是只有一个元素。过滤是使用内部 for 理解的第二个参数而不是使用 Enum.filter,这使代码更清晰。

for c <- categories do
  prod = for p <- products, p.category == c.id do
    p.name
  end
  %{name: c.name, products: prod}
end