Laxmikant Ratnaparkhi
Laxmikant Ratnaparkhi

Reputation: 5003

Merge inner lists of a list erlang

I have a list L

L = [L1, L2, L3,...]. 

where L1, L2, L3.. are themselves lists, like so:

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 = [...]

now I want a result combined list as :

FinalList = [
       {k1, [10, 90, ...]}, % '...' denotes values from other lists
       {k2, [20, 210, ...]},
       {K3, [30, 60, ...]},
       {k4, [20.9, 66.9, ...]},
       {K6, ["Hello world", "Hello universe", ...]}
     ]

I can merge using my Old Posts Solutions:Combine/Merge Two Erlang lists But I'm not sure how I would pass L (a list of lists) to that merge function.

Thank you in advance!

Upvotes: 2

Views: 605

Answers (3)

Berzemus
Berzemus

Reputation: 3668

I got inspired, and got this working function, assuming all lists have the same keys (which seems to be the case here):

merge_lists([First|_]=ListOfLists) ->
    Keys = proplists:get_keys(First),
    lists:map(fun(Key) ->
                  {key,lists:flatmap(fun(List) -> 
                                         [proplists:get_value(Key,List)] 
                                         end
                                    ,ListOfLists)} 
                  end
             ,Keys).

Excuse the formatting: this function starts by getting the keys it should look for, and then compiles, per key, the list of values from each list (no need to update an accumulator in this way). Could be a one-liner, but that would be a long line ;)

The result with some test lists (L3 = L1, but doesn't have key k6, which is why it is undefined):

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

Edit: This is perhaps a more digestible format. Looks better too:

merge_lists([First|_]=ListOfLists) ->
    MapKeys = fun(Key) -> 
                MapLists = fun(List) -> [proplists:get_value(Key,List)] end,
                Values   = lists:flatmap(MapLists ,ListOfLists),
                {Key,Values} end,
    lists:map(MapKeys,proplists:get_keys(First)).

Upvotes: 3

Anthony Kong
Anthony Kong

Reputation: 40654

You can use lists:foldl to apply merge to a list of lists. Here is an example:

-module(test).
-export([test/0]).


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

merge_([{K, V1}|T1], [{K, V2}|T2]) when is_list(V1), is_list(V2)
    -> [{K, V1 ++ V2}|merge_(T1, T2)]; 
merge_([{K, V1}|T1], [{K, V2}|T2]) when is_list(V1)
    -> [{K, V1 ++ [V2]}|merge_(T1, T2)];
merge_([{K, V1}|T1], [{K, V2}|T2]) when is_list(V2)
    -> [{K, [V1] ++ V2}|merge_(T1, T2)];
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]) when is_list(V2)
   -> [{K2, V2}|merge_(L1, T2)];
merge_(L1, [{K2, V2}|T2]) 
   -> [{K2, [V2]}|merge_(L1, T2)];
merge_(L1, []) -> [{K, V} || {K, V} <- L1].


test() ->

  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, 45}, {k2, 35}, {k3, 37}, {k4, 77.9}, {k6, "Hello cosmo"}],
  lists:foldl(fun merge/2, [], [L1, L2, L3]).

And here is the outcome:

36> test:test().
[{k1,"-Z\n"},
 {k2,[35,210,20]},
 {k3,[37,60,30]},
 {k4,[77.9,66.9,20.9]},
 {k6,"Hello cosmoHello universeHello world"}]

As you can see,

  1. You need to modify the merge function to handle list (not just atom as in your original question). It is already done in my example code, which is based on Vychodil's answer.

  2. You need to modify the merge function to handle string properly (as evidence in keys k1 and k6). You should be able to fix it yourself.

Last but not least, you should accept an answer when it resolves your question. Check this link for why.

Upvotes: 3

Alexey Romanov
Alexey Romanov

Reputation: 170735

Berzemus' answer from the linked question can be easily adapted:

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

However, this will be quite inefficient if there are many lists in L or they are long. In this case you should start from Vychodil's solution.

Upvotes: 2

Related Questions