左外连接与 Elixir 的理解
Left outer join with Elixir's for comprehension
我一直在研究我可以用 Enum
、Stream
和 for
理解做什么,我正在努力重现左外连接行为。我可以用 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
我一直在研究我可以用 Enum
、Stream
和 for
理解做什么,我正在努力重现左外连接行为。我可以用 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