2014-02-19 4 views
5

Как совместить списки кортежей в erlang? У меня есть списки:Объединение/Объединение двух списков Erlang

L1 = [{k1, 10}, {k2, 20}, {k3, 30}, {k4, 20.9}, {k6, "Hello world"}], 

и

L2 = [{k1, 90}, {k2, 210}, {k3, 60}, {k4, 66.9}, {k6, "Hello universe"}], 

теперь я хочу комбинированный список, как:

L3 = [ 
     {k1, [10, 90]}, 
     {k2, [20, 210]}, 
     {K3, [30, 60]}, 
     {k4, [20.9, 66.9]}, 
     {K6, ["Hello world", "Hello universe"]} 
    ]. 

ответ

4

что-то короче, и списки даже не должны обладать теми же ключами, и может быть неупорядоченным:

merge(In1,In2) -> 
    Combined = In1 ++ In2, 
    Fun  = fun(Key) -> {Key,proplists:get_all_values(Key,Combined)} end, 
    lists:map(Fun,proplists:get_keys(Combined)). 

Fun может быть записана непосредственно в функции lists:map/2, но это делает его доступным для чтения.

выход, с данными из примера:

1> test:merge(L1,L2). 
[{k1,"\nZ"}, 
{k2,[20,210]}, 
{k3,[30,60]}, 
{k4,[20.9,66.9]}, 
{k6,["Hello world","Hello universe"]}] 

"\nZ" происходит потому, что Эрланга интерпретирует [10,90] в виде строки (которые, по сути, списки). Не беспокойтесь.

+0

Спасибо @carlo и Berzemus за предложения –

2

Может быть, это не самый лучший способ, но он делает то, что вы пытаетесь достичь.

merge([{A, X}| T1], [{A, Y} | T2], Acc) -> 
    New_acc = [{A, [X, Y]} | Acc], 
    merge(T1, T2, New_acc); 

merge([{A, X} | T1], [{B, Y} | T2], Acc) -> 
    New_acc = [{A, [X]}, {B, Y} | Acc], 
    merge(T1, T2, New_acc); 

merge([], [{B, Y} | T], Acc) -> 
    New_acc = [{B, Y} | Acc], 
    merge([], T, New_acc); 

merge([{A, X} | T], [], Acc) -> 
    New_acc = [{A, X} | Acc], 
    merge(T, [], New_acc); 

merge([], [], Acc) -> 
    lists:reverse(Acc). 

Редактировать Я предполагаю, что входные списки упорядочены, как в вашем входе образца. Если нет, вы можете использовать lists:sort/2, чтобы отсортировать их до слияния.

+0

Спасибо, я попробую это предложение! –

+0

Я получил результат, но не уверен, почему я получаю \ nZ в первом кортеже: '10> a: comb_lists(). [{k1, "\ пЪ"}, { k2, [20210]}, { k3, [30,60]}, { k4, [20.9,66.9]}, { k6, [ "Привет мир "," Hello universe "]}].' Pl. Помогите. Спасибо –

+0

_ Я предполагаю, что списки входных данных упорядочены так, как в вашем примере ввода. @ Карлос: Ох, тогда Извините, они не заказываются –

4

Этот метод называется объединением объединения. Это хорошо известно в дизайне базы данных.

merge(L1, L2) -> 
    merge_(lists:sort(L1), lists:sort(L2)). 

merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)]; 
merge_([], []) -> []. 

Если могут быть различные наборы ключей в обоих списках, и вы готовы отказаться от этих значений вы можете использовать

merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)]; 
merge_([{K1, _}|T1], [{K2, _}|_]=L2) when K1 < K2 -> merge_(T1, L2); 
merge_(L1, [{_, _}|T2]) -> merge_(L1, T2);` 
merge_(_, []) -> []. 

Или, если вы хотите сохранить эти значения в списках

merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)]; 
merge_([{K1, V1}|T1], [{K2, _}|_]=L2) when K1 < K2 -> [{K1, [V1]}|merge_(T1, L2)]; 
merge_(L1, [{K2, V2}|T2]) -> [{K2, [V2]}|merge_(L1, T2)]; 
merge_(L1, []) -> [{K, [V]} || {K, V} <- L1]. 

Вы можете, конечно, использовать рекурсивную версию хвоста, если вы не возражаете против результата в обратном порядке, или вы всегда можете использовать lists:reverse/1

merge(L1, L2) -> 
    merge(lists:sort(L1), lists:sort(L2), []). 

merge([{K, V1}|T1], [{K, V2}|T2], Acc) -> merge(T1, T2, [{K, [V1, V2]}|Acc]); 
merge([], [], Acc) -> Acc. % or lists:reverse(Acc). 

Или

merge([{K, V1}|T1], [{K, V2}|T2], Acc) -> merge(T1, T2, [{K, [V1, V2]}|Acc]); 
merge([{K1, _}|T1], [{K2, _}|_]=L2, Acc) when K1 < K2 -> merge(T1, L2, Acc); 
merge(L1, [{_, _}|T2], Acc) -> merge(L1, T2, Acc);` 
merge(_, [], Acc) -> Acc. % or lists:reverse(Acc). 

Или

merge([{K, V1}|T1], [{K, V2}|T2], Acc) -> merge(T1, T2, [{K, [V1, V2]}|Acc]); 
merge([{K1, V1}|T1], [{K2, _}|_]=L2, Acc) when K1 < K2 -> merge(T1, L2, [{K1, [V1]}|Acc]); 
merge(L1, [{K2, V2}|T2], Acc) -> merge(L1, T2, [{K2, [V2]}|Acc]);` 
merge([{K1, V1}|T1], [], Acc) -> merge(T1, [], [{K1, [V1]} | Acc]); 
merge([], [], Acc) -> Acc. % or lists:reverse(Acc). 
% or merge(L1, [], Acc) -> lists:reverse(Acc, [{K, [V]} || {K, V} <- L1]). 
% instead of two last clauses. 

Если есть вероятность того, что один из списков может содержать одинаковые ключи, и вы готовы собрать все значения, которые вы можете рассмотреть этот

merge(L1, L2) -> 
    merge(lists:sort(L1), lists:sort(L2), []). 

merge([{K1, _}|_]=L1, {K2, _}|_]=L2, Acc) -> 
    K = min(K1, K2), 
    {Vs1, T1} = collect(K, L1, []), 
    {Vs2, T2} = collect(K, L2, Vs1), 
    merge(T1, T2, [{K, Vs2}|Acc]); 
merge([{K, _}|_]=L1, [], Acc) -> 
    {Vs, T1} = collect(K, L1, []), 
    merge(T1, [], [{K, Vs}|Acc]); 
merge([], [{K, _}|_]=L2, Acc) -> 
    {Vs, T2} = collect(K, L2, []), 
    merge([], T2, [{K, Vs}|Acc]); 
merge([], [], Acc) -> lists:reverse(Acc). 

collect(K, [{K, V}|T], Acc) -> collect(K, T, [V|Acc]); 
collect(_, T, Acc) -> {Acc, T}. 
3

Что случилось с lists:zipwith/2?

Предположения:

  • списки имеют одинаковую длину
  • списки содержат одни и те же ключи в том же порядке

lists:zipwith(fun({X, Y}, {X, Z}) -> {X, [Y, Z]} end, L1, L2).

+0

Очень круто и красиво, в одной строке. Спасибо –

5

Существует хорошее решение к этому, используя модуль sofs в библиотеке Erlang Standard. Модуль sofs описывает DSL для работы с математическими наборами. Это одна из тех ситуаций, когда вы можете использовать ее, преобразуя свои данные в мир SOFS, манипулируя ими внутри этого мира, а затем снова преобразуйте их обратно снаружи.

Обратите внимание, что я немного изменил ваш L3, так как sofs не сохраняет порядок строк.

-module(z). 

-compile(export_all). % Don't do this normally :) 

x() -> 
    L1 = [{k1, 10}, {k2, 20}, {k3, 30}, {k4, 20.9}, {k6, "Hello world"}], 
    L2 = [{k1, 90}, {k2, 210}, {k3, 60}, {k4, 66.9}, {k6, "Hello universe"}], 
    L3 = [{k1, [10, 90]},{k2, [20, 210]},{k3, [30, 60]},{k4, [20.9, 66.9]},{k6, ["Hello universe", "Hello world"]}], 
    R = sofs:relation(L1 ++ L2), 
    F = sofs:relation_to_family(R), 
    L3 = sofs:to_external(F), 
    ok. 
+0

Это кажется довольно интересным! Благодарю. Здесь порядок строк не так важен. Здорово! Спасибо –

+0

Это действительно хорошо, мне никогда не приходило в голову использовать расширенные операции набора, возможно, потому, что я считаю очень математически склонным;) – Berzemus

+0

@IGIVECRAPANSWERS - не знал о сафах! прохладный предмет. – trex

Смежные вопросы