我有两个列表:
alist = [ %{"a" => 1}, %{"b" => 2}, %{"c" => 3}]
blist = [ %{"a" => 3}, %{"c" => 4}]
如果blist
有新的值,比较它们并更新alist
的最有效方法是什么?
最终结果类似于:
[ %{"a" => 3}, %{"b" => 2}, %{"c" => 4}] #updated_alist
我知道我应该用Enum.map/2
,但就是想不起来。
Update01在上面的例子中,我可能过于简化了我的情况。
我的真实情况是,我从网站获得JSON
数据,这些数据是:
content_a = {content:[
{item: "apple", committed: false},
{item: "orange", committed: false}
]}
content_b = {content:[
{item: "orange", committed: true}
]}
我想把它更新为:
updated_content = {content:[
{item: "apple", committed: false},
{item: "orange", committed: true} # <-- updated this.
]}
我确实尝试了Map.merge
,但我的最终结果是:
merged_content = {content:[
{item: "orange", committed: true}
]}
#{item: "apple", committed: false} is missing
我不知道发生了什么。
我同意Aleksei的观点,你的数据结构看起来很奇怪。如果您使用标准关键字列表或标准映射,则可以利用标准合并函数(Keyword。merge/2或Map.merge/2)
。合并关键字列表:
iex> alist = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex> blist = [a: 3, c: 4]
[a: 3, c: 4]
iex> Keyword.merge(alist, blist)
[b: 2, a: 3, c: 4]
。合并地图:
iex> amap = %{a: 1, b: 2, c: 3}
%{a: 1, b: 2, c: 3}
iex> bmap = %{a: 3, c: 4}
%{a: 3, c: 4}
iex> Map.merge(amap, bmap)
%{a: 3, b: 2, c: 4}
最有效的方法是生成blist
的映射,并在迭代alist
时使用它,以避免每次遍历整个blist
列表。
blist_map =
for bmap <- blist, {k, v} <- bmap, do: {k, v}, into: %{}
#⇒ %{"a" => 3, "c" => 4}
for amap <- alist, {k, v} <- amap,
do: %{k => Map.get(blist_map, k, v)}
#⇒ [%{"a" => 3}, %{"b" => 2}, %{"c" => 4}]
旁注:alist
和blist
的结构闻起来都不对。你为什么想要地图列表?裸地图不是更好的选择吗?毕竟,有了地图,就可以做Map.merge/3
了。
涉及Enum.map/2
的低效直接解决方案是:
Enum.map(alist, fn map ->
{k, _} = Enum.at(map, 0)
Enum.find(blist, &match?(%{^k => _}, &1)) || map
end)
#⇒ [%{"a" => 3}, %{"b" => 2}, %{"c" => 4}]