更新列表的值

时间:2011-10-08 08:47:02

标签: erlang list-comprehension

我有以下设置:

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中元素的先前值。我要做的是使用列表L中的值更新列表M,同时保留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的简单方法。

非常感谢。

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

纯列表推导解决方案:

[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).

答案 2 :(得分:0)

您可以使用lists:ukeymerge/3

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

其中:

  

返回通过合并TupleList1和TupleList2形成的排序列表。   合并在每个元组的第N个元素上执行。都   TupleList1和TupleList2必须按键排序,不需要重复   评估这个功能。当两个元组比较相等时,元组   选择TupleList1,删除TupleList2中的那个。

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