更新列表的值

发布于 2024-12-09 01:58:37 字数 944 浏览 0 评论 0原文

我有以下设置:

1> rd(rec, {name, value}).
rec
2> L = [#rec{name = a, value = 1}, #rec{name = b, value = 2}, #rec{name = c, value = 3}].
[#rec{name = a,value = 1},
 #rec{name = b,value = 2},
 #rec{name = c,value = 3}]
3> M = [#rec{name = a, value = 111}, #rec{name = c, value = 333}].
[#rec{name = a,value = 111},#rec{name = c,value = 333}]

列表 L 中的元素根据其 name 是唯一的。我也不知道列表 M 中元素的先前值。我想做的是用列表 M 中的值更新列表 L ,同时保留 L 中不存在的元素M。我做了以下工作:

update_values([], _M, Acc) ->
    Acc;
update_attributes_from_fact([H|T], M, Acc) ->
    case [X#rec.value || X <- M, X#rec.name =:= H#rec.name] of
      [] ->
        update_values(T, M, [H|Acc]);
      [NewValue] ->
        update_values(T, M, [H#rec{value = NewValue}|Acc])
    end.

它完成了这项工作,但我想知道是否有一种使用 bifs 的更简单的方法。

多谢。

I have the following setup:

1> rd(rec, {name, value}).
rec
2> L = [#rec{name = a, value = 1}, #rec{name = b, value = 2}, #rec{name = c, value = 3}].
[#rec{name = a,value = 1},
 #rec{name = b,value = 2},
 #rec{name = c,value = 3}]
3> M = [#rec{name = a, value = 111}, #rec{name = c, value = 333}].
[#rec{name = a,value = 111},#rec{name = c,value = 333}]

The elements in list L are unique based on their name. I also don't know the previous values of the elements in list M. What I am trying to do is to update list L with the values in list M, while keeping the elements of L that are not present in M. I did the following:

update_values([], _M, Acc) ->
    Acc;
update_attributes_from_fact([H|T], M, Acc) ->
    case [X#rec.value || X <- M, X#rec.name =:= H#rec.name] of
      [] ->
        update_values(T, M, [H|Acc]);
      [NewValue] ->
        update_values(T, M, [H#rec{value = NewValue}|Acc])
    end.

It does the job but I wonder if there is a simpler method that uses bifs.

Thanks a lot.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暮色兮凉城 2024-12-16 01:58:37

没有现有的函数可以为您执行此操作,因为您只想更新值字段而不是替换 L 中的整个记录​​(如lists:keyreplace() 所做的那样)。如果 L 和 M 都可以很长,我建议如果可以的话,使用 #rec.name 作为键将 L 从列表更改为 dict 或 gb_tree。然后,您可以循环 M,对于 M 中的每个元素,查找正确的条目(如果有)并写回更新的记录。循环可以写成折叠。即使您先将列表 L 转换为字典,然后在循环后再次将其转换回来,它也会比 L*M 方法更有效。但是,如果 M 总是很短,并且您不想在代码的其余部分中将 L 作为字典保留,那么您当前的方法很好。

There's no existing function that does this for you, since you just want to update the value field rather than replacing the entire record in L (like lists:keyreplace() does). If both L and M can be long, I recommend that if you can, you change L from a list to a dict or gb_tree using #rec.name as key. Then you can loop over M, and for each element in M, look up the correct entry if there is one and write back the updated record. The loop can be written as a fold. Even if you convert the list L to a dict first and convert it back again after the loop, it will be more efficient than the L*M approach. But if M is always short and you don't want to keep L as a dict in the rest of the code, your current approach is good.

我早已燃尽 2024-12-16 01:58:37

纯列表理解解决方案:

[case [X||X=#rec{name=XN}<-M, XN=:=N] of [] -> Y; [#rec{value =V}|_] -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

使用 lists:keyfind/3 更有效:

[case lists:keyfind(N,#rec.name,M) of false -> Y; #rec{value=V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

对于大 M 更有效:

D = dict:from_list([{X#rec.name, X#rec.value} || X<-M]),
[case dict:find(N,D) of error -> Y; {ok,V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

但对于真正的大 M,这种方法可能是最快的:

merge_join(lists:keysort(#rec.name, L), lists:ukeysort(#rec.name, M)).

merge_join(L, []) -> L;
merge_join([], _) -> [];
merge_join([#rec{name=N}=Y|L], [#rec{name=N, value=V}|_]=M) -> [Y#rec{value=V}|merge_join(L,M)];
merge_join([#rec{name=NL}=Y|L], [#rec{name=NM}|_]=M) when NL<NM -> [Y|merge_join(L,M)];
merge_join(L, [_|M]) -> merge_join(L, M).

Pure list comprehensions solution:

[case [X||X=#rec{name=XN}<-M, XN=:=N] of [] -> Y; [#rec{value =V}|_] -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

little bit more effective using lists:keyfind/3:

[case lists:keyfind(N,#rec.name,M) of false -> Y; #rec{value=V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

even more effective for big M:

D = dict:from_list([{X#rec.name, X#rec.value} || X<-M]),
[case dict:find(N,D) of error -> Y; {ok,V} -> Y#rec{value=V} end || Y=#rec{name=N} <- L].

but for really big M this approach can be fastest:

merge_join(lists:keysort(#rec.name, L), lists:ukeysort(#rec.name, M)).

merge_join(L, []) -> L;
merge_join([], _) -> [];
merge_join([#rec{name=N}=Y|L], [#rec{name=N, value=V}|_]=M) -> [Y#rec{value=V}|merge_join(L,M)];
merge_join([#rec{name=NL}=Y|L], [#rec{name=NM}|_]=M) when NL<NM -> [Y|merge_join(L,M)];
merge_join(L, [_|M]) -> merge_join(L, M).
回忆躺在深渊里 2024-12-16 01:58:37

您可以使用 lists:uke​​ymerge/3

lists:ukeymerge(#rec.name, M, L).

其中:

返回通过合并 TupleList1 和 TupleList2 形成的排序列表。
对每个元组的第 N 个元素执行合并。两个都
TupleList1 和 TupleList2 必须先进行键排序,不得重复
来评估这个函数。当两个元组比较相等时,元组
从 TupleList1 中选择并从 TupleList2 中删除。

记录是一个元组,您可以使用#rec.name 以透明的方式返回键的位置。请注意,我恢复了列表 L 和 M,因为该函数保留了第一个列表中的值。

You could use lists:ukeymerge/3:

lists:ukeymerge(#rec.name, M, L).

Which:

returns the sorted list formed by merging TupleList1 and TupleList2.
The merge is performed on the Nth element of each tuple. Both
TupleList1 and TupleList2 must be key-sorted without duplicates prior
to evaluating this function. When two tuples compare equal, the tuple
from TupleList1 is picked and the one from TupleList2 deleted.

A record is a tuple and you can use #rec.name to return the position of the key in a transparent way. Note that I reverted the lists L and M, since the function keeps the value from the first list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文