Elixir语言 - 比较两个列表并更新最新的项目



我有两个列表:

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}]

旁注:alistblist的结构闻起来都不对。你为什么想要地图列表?裸地图不是更好的选择吗?毕竟,有了地图,就可以做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}]

最新更新