Reputation: 5003
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
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
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,
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.
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
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