使用 Enum.find_value 合并地图列表
Merge list of map using Enum.find_value
根据id合并列表
products =
[
%{id: 7, name: "A", count: 1},
%{id: 8, name: "B", count: 1},
%{name: "C", count: 0}
]
price =
[
%{price: ".00", p_id: 7},
%{price: ".95", p_id: 10},
]
目前正在使用
Enum.map(products, &Map.put(&1, :price, Enum.find(price, fn %{p_id: pid} -> pid == &1.id end).price))
但如果 id
不在产品列表中,则会引发错误。
(KeyError) key :id not found in: %{count: 1, name: "A"}
如何使用Enum.find_value
?
如果前面的列表后面引用为product
,latter
为price
,当后者没有p_id
对应的元素时出现问题前一个中的元素,一个可能与
product =
[
%{id: 7, name: "A", count: 1},
%{id: 8, name: "B", count: 1},
%{id: 9, name: "C", count: 0}
]
price =
[
%{price: ".95", p_id: 8},
%{price: ".00", p_id: 7}
]
在那种情况下,Enum.map/2
会引发,但不会在 Enum.find/2
上引发,它会在找不到元素时愉快地返回 nil
,但在 a) 内部函数子句模式匹配上和 b) 在 .price
访问时 Enum.find/2
returns nil
.
讨厌的解决方案是使用 Access
而不是点符号来消除这两个问题:
Enum.map(
products,
&Map.put(
&1,
:price,
# ⇓⇓⇓⇓⇓⇓⇓⇓ ⇓⇓⇓⇓⇓⇓⇓⇓
Enum.find(price, fn p -> p[:p_id] == &1.id end)[:price]
)
)
另一种方法是正确查找元素并相应地处理它
Enum.map(
products, fn %{id: id} = product ->
product_price =
price
|> Enum.find(&match?(%{p_id: ^id}, &1))
|> case do
%{price: price} -> price
_ -> nil
end
Map.put(product, :price, product_price)
end)
OTOH,如果 id
不存在于 product
中,它会引发 KeyError
因为 &1.id
调用。为避免这种情况,可以使用后一种代码并添加显式 sink-all 子句来处理此问题。这个版本在健壮性和容易出错方面是最好的:
Enum.map(
products, fn
%{id: id} = product ->
product_price =
price
|> Enum.find(&match?(%{p_id: ^id}, &1))
|> case do
%{price: price} -> price
_ -> nil # no price presented
end
Map.put(product, :price, product_price)
product -> product # no id presented
end)
#⇒ [
# %{count: 1, id: 7, name: "A", price: ".00"},
# %{count: 1, id: 8, name: "B", price: nil},
# %{count: 0, name: "C"}
# ]
根据id合并列表
products =
[
%{id: 7, name: "A", count: 1},
%{id: 8, name: "B", count: 1},
%{name: "C", count: 0}
]
price =
[
%{price: ".00", p_id: 7},
%{price: ".95", p_id: 10},
]
目前正在使用
Enum.map(products, &Map.put(&1, :price, Enum.find(price, fn %{p_id: pid} -> pid == &1.id end).price))
但如果 id
不在产品列表中,则会引发错误。
(KeyError) key :id not found in: %{count: 1, name: "A"}
如何使用Enum.find_value
?
如果前面的列表后面引用为product
,latter
为price
,当后者没有p_id
对应的元素时出现问题前一个中的元素,一个可能与
product =
[
%{id: 7, name: "A", count: 1},
%{id: 8, name: "B", count: 1},
%{id: 9, name: "C", count: 0}
]
price =
[
%{price: ".95", p_id: 8},
%{price: ".00", p_id: 7}
]
在那种情况下,Enum.map/2
会引发,但不会在 Enum.find/2
上引发,它会在找不到元素时愉快地返回 nil
,但在 a) 内部函数子句模式匹配上和 b) 在 .price
访问时 Enum.find/2
returns nil
.
讨厌的解决方案是使用 Access
而不是点符号来消除这两个问题:
Enum.map(
products,
&Map.put(
&1,
:price,
# ⇓⇓⇓⇓⇓⇓⇓⇓ ⇓⇓⇓⇓⇓⇓⇓⇓
Enum.find(price, fn p -> p[:p_id] == &1.id end)[:price]
)
)
另一种方法是正确查找元素并相应地处理它
Enum.map(
products, fn %{id: id} = product ->
product_price =
price
|> Enum.find(&match?(%{p_id: ^id}, &1))
|> case do
%{price: price} -> price
_ -> nil
end
Map.put(product, :price, product_price)
end)
OTOH,如果 id
不存在于 product
中,它会引发 KeyError
因为 &1.id
调用。为避免这种情况,可以使用后一种代码并添加显式 sink-all 子句来处理此问题。这个版本在健壮性和容易出错方面是最好的:
Enum.map(
products, fn
%{id: id} = product ->
product_price =
price
|> Enum.find(&match?(%{p_id: ^id}, &1))
|> case do
%{price: price} -> price
_ -> nil # no price presented
end
Map.put(product, :price, product_price)
product -> product # no id presented
end)
#⇒ [
# %{count: 1, id: 7, name: "A", price: ".00"},
# %{count: 1, id: 8, name: "B", price: nil},
# %{count: 0, name: "C"}
# ]