用于基于ID合并列表
products =
[
%{id: 7, name: "A", count: 1},
%{id: 8, name: "B", count: 1},
%{name: "C", count: 0}
]
price =
[
%{price: "$10.00", p_id: 7},
%{price: "$29.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: "$14.95", p_id: 8},
%{price: "$10.00", p_id: 7}
]
在那种情况下,Enum.map/2
会升高,但不会在Enum.find/2
上升高,当找不到元素时会很高兴地返回nil
,但是在a)内部函数子句模式匹配上并且b)在.price
上访问时Enum.find/2
返回nil
。
最讨厌的解决方案是使用Access
代替点符号来消除这两个问题:
Access
另一种方法是正确查找元素并进行相应处理
Enum.map(
products,
&Map.put(
&1,
:price,
# ⇓⇓⇓⇓⇓⇓⇓⇓ ⇓⇓⇓⇓⇓⇓⇓⇓
Enum.find(price, fn p -> p[:p_id] == &1.id end)[:price]
)
)
OTOH,如果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)
中不存在id
,则由于调用product
而将其升高KeyError
。为了避免这种情况,可以使用后一种代码并添加显式的sink-all子句来处理此问题。就健壮性和易于出错而言,此版本是最好的:
&1.id