Combine/Merge Two Erlang lists - erlang
How to combine tuple lists in erlang? I have lists:
L1 = [{k1, 10}, {k2, 20}, {k3, 30}, {k4, 20.9}, {k6, "Hello world"}],
and
L2 = [{k1, 90}, {k2, 210}, {k3, 60}, {k4, 66.9}, {k6, "Hello universe"}],
now I want a combined list as :
L3 = [
{k1, [10, 90]},
{k2, [20, 210]},
{K3, [30, 60]},
{k4, [20.9, 66.9]},
{K6, ["Hello world", "Hello universe"]}
].
There is a nice solution to this one by using the sofs module in the Erlang Standard library. The sofs module describes a DSL for working with mathematical sets. This is one of those situations, where you can utilize it by transforming your data into the SOFS-world, manipulate them inside that world, and then transform them back again outside afterwards.
Note that I did change your L3 a bit, since sofs does not preserve the string order.
-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.
Something shorter, and the lists don't even have to posses the same keys, and can be unordered:
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 could be written directly in the lists:map/2 function, but this makes it readable.
Output, with data from example:
1> test:merge(L1,L2).
[{k1,"\nZ"},
{k2,[20,210]},
{k3,[30,60]},
{k4,[20.9,66.9]},
{k6,["Hello world","Hello universe"]}]
"\nZ" is because erlang interprets [10,90] as a string (which are, in fact, lists). Don't bother.
This technique is called merge join. It is well known in database design.
merge(L1, L2) ->
merge_(lists:sort(L1), lists:sort(L2)).
merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)];
merge_([], []) -> [].
If there can be different sets of keys in both lists and you are willing to drop those values you can use
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_(_, []) -> [].
Or if you would like store those values in lists
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].
You can of course use tail recursive version if you don't mind result in reverse order or you can always use 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).
Or
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).
Or
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.
If there is possibility that one of lists can contain same keys and you are willing collect all values you can consider this
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}.
What happened to lists:zipwith/2?
Assumptions:
lists are the same length
lists contain the same keys in the same order
lists:zipwith(fun({X, Y}, {X, Z}) -> {X, [Y, Z]} end, L1, L2).
Maybe this is not the best way, but it does what you are trying to achieve.
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).
Edit
I'm assuming that the input lists are ordered as in your sample input. If not you can use lists:sort/2 to sort them before merging.
You can use lists comprehensions:
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"}],
[ {K,[V1,V2]} || {K,V1} <- L1, {K2,V2} <- L2, K == K2].
Output:
[{k1,"\nZ"}, % [10,90] shown in console as string..
{k2,[20,210]},
{k3,[30,60]},
{k4,[20.9,66.9]},
{k6,["Hello world","Hello universe"]}]
Related
Function to convert map to a list in erlang
Suppose I have a map like this A = #{a=>1,b=>2,c=>3}. I want to create a function which converts A to a list of tuples of key-value pairs. list = [{a,1},{b,2},{c,3}]
maps:to_list/1 does exactly this: 1> maps:to_list(#{a=>1,b=>2,c=>3}). [{a,1},{b,2},{c,3}]
You can use maps:fold/3 for loop map items. Let's say you need just convert a map, then you can use something like: 1> A = #{a=>1,b=>2,c=>3}. 2> maps:fold( fun(K, V, Acc) -> [{K, V} | Acc] end, [], A). [{c,3},{b,2},{a,1}] For case if need to do the same for nested maps, this example can be modify like: 1> A = #{a => 1, b => 2, c => 3, d => #{a => 1, b => #{a => 1}}}, 2> Nested = fun F(K, V = #{}, Acc) -> [{K, maps:fold(F, [], V)} | Acc]; F(K, V, Acc) -> [{K, V} | Acc] end, 3> maps:fold(Nested, [], A). [{d,[{b,[{a,1}]},{a,1}]},{c,3},{b,2},{a,1}]
Counting Number of Items in a tuple List in Erlang
I have the following List structure: [{A, [{B <--, [A, C]}, {C <--, [B, A]}]}, {B, [{C <--, [A, C]}]}] For example, B = 1, C = 2. What would be the correct way to do so? UPDATE I'd like to count the number of <-- (a symbol I added just to show what I'm referring to) pointed item in it.
It can be implemented in many ways. Here is one more. count(List) -> count(List, dict:new()). count([{_, InList} | Rest], Dict) -> count(Rest, count2(InList, Dict)); count([], Dict) -> dict:to_list(Dict). count2([{Symbol, _} | Rest], Dict) -> count2(Rest, dict:update_counter(Symbol, 1, Dict)); count2([], Dict) -> Dict. Example Output: 1> test:count([{one, [{b, [123]}, {c,[123]}, {b,[123]}]}, {two, [{b, [123]}, {c,[123]}, {b,[123]}]}]). [{b,4},{c,2}]
You can write a simple code using an accumulator and some list functions. Supposing all list elements are of the same format: count(L) -> count (L, []). count ([], Cases) -> Cases; count ([{_E1, [{X, [_E2, _E3]}]} | Rest], Cases) -> NewCases = case lists:keysearch(X, 1, Cases) of false -> [ {X, 1} | Cases ]; {value, {X, Val}} -> lists:keyreplace(X, 1, Cases, {X, 1+Val}) end, count(Rest, NewCases).
List combination (n choose k) that returns tuple (chosen, not chosen) instead of just (chosen)
I want to alter the following function (or create an entirely new one) so that it returns (chosen, not chosen) instead of just (chosen): let rec comb n l = match n, l with | 0, _ -> [[]] | _, [] -> [] | k, (x::xs) -> List.map ((#) [x]) (comb (k-1) xs) # comb k xs Currently, comb 2 ["R";"G";"B"] returns [["R"; "G"]; ["R"; "B"]; ["G"; "B"]] I want it to return [(["R"; "G"], ["B"]); (["R"; "B"], ["G"]); (["G"; "B"], ["R"])] Most of my problem is that I have a lack of understanding about what is going on in the last line of the current function.
Should look something like this: let rec comb n l = match n, l with | 0, l -> [[],l] | _, [] -> [] | k, (x::xs) -> [for (yes,no) in comb (k-1) xs do yield x::yes,no for (yes,no) in comb k xs do yield yes,x::no]
Erlang Bubble sort
I'm learning Erlang and decided to implement bubble sort in it, it took me some effort and in result i've succeded, but i see that my way of thinking is incorrect, is there more effecient way to implement it or not? bubble_sort(L) -> if length(L) > 1 -> SL=bubble_sort_p(L), bubble_sort(lists:sublist(SL,1,length(SL)-1)) ++ [lists:last(SL)]; true -> L end. bubble_sort_p([]) -> []; bubble_sort_p([F | R]) -> case length(R) > 0 of true -> case F > hd(R) of true -> [hd(R)] ++ bubble_sort_p([F|tl(R)]); false -> [F] ++ bubble_sort_p([hd(R)|tl(R)]) end; false -> [F] end.
The implementation from the top of my head would be: bubble_sort(L) -> bubble_sort(L, [], false). bubble_sort([A, B | T], Acc, _) when A > B -> bubble_sort([A | T], [B | Acc], true); bubble_sort([A, B | T], Acc, Tainted) -> bubble_sort([B | T], [A | Acc], Tainted); bubble_sort([A | T], Acc, Tainted) -> bubble_sort(T, [A | Acc], Tainted); bubble_sort([], Acc, true) -> bubble_sort(lists:reverse(Acc)); bubble_sort([], Acc, false) -> lists:reverse(Acc). If we are talking efficiency, we obvioulsy should not go for bubble sort in the first place.
Allow me to re-write your code in a much more readable fashion (without changing anything about the semantics): bubble_sort(L) when length(L) =< 1 -> L; bubble_sort(L) -> SL = bubble_sort_p(L), bubble_sort(lists:sublist(SL,1,length(SL)-1)) ++ [lists:last(SL)]. bubble_sort_p([]) -> []; bubble_sort_p([F]) -> [F]; bubble_sort_p([F,G|T]) when F > G -> [G|bubble_sort_p([F|T])]; bubble_sort_p([F,G|T]) -> [F|bubble_sort_p([G|T])]. This should allow for much easier contemplation of the things actually going on in the program.
And employing lists:foldr: bubble(List) -> Step = fun (E,{S,[]}) -> {S,[E]}; (E,{_,[X|XS]}) when E > X -> {swapped,[X|[E|XS]]}; (E,{S,XS}) -> {S,[E|XS]} end, case lists:foldr(Step, {intact,[]}, List) of {intact,XS} -> XS; {swapped,XS} -> bubble(XS) end.
There is an answer at http://en.literateprograms.org/Bubble_sort_%28Erlang%29 -module(bubblesort). -export([sort/1]). -import(lists, [reverse/1]). sort(L) -> sort(L, [], true). sort([], L, true) -> reverse(L); sort([], L, false) -> sort(reverse(L), [], true); sort([ X, Y | T ], L, _) when X > Y -> sort([ X | T ], [ Y | L ], false); sort([ X | T ], L, Halt) -> sort(T, [ X | L ], Halt). An example for sorting 2,4,3,5,1 sort([2,4,3,5,1]) round 1 => sort([2,4,3,5,1], [], true) => sort([4,3,5,1], [2], true) => sort([4,5,1], [3,2], false) => sort([5,1], [4,3,2], false) => sort([5], [1,4,3,2], false) => sort([], [5,1,4,3,2], false) => sort([2,3,4,1,5], [], true) round 2 => sort([3,4,1,5], [2], true) => sort([4,1,5], [3,2], true) => sort([4,5], [1,3,2], false) => sort([5], [4,1,3,2], false) => sort([], [5,4,1,3,2], false) => sort([2,3,1,4,5], [], true) round 3 => sort([3,1,4,5], [2], true) => sort([3,4,5], [1,2], false) => sort([4,5], [3,1,2], false) => sort([5], [4,3,1,2], false) => sort([], [5,4,3,1,2], false) => sort([2,1,3,4,5], true) round 4 => sort([2,3,4,5], [1], false) => sort([3,4,5], [2,1], false) => sort([4,5], [3,2,1], false) => sort([5], [4,3,2,1], false) => sort([], [5,4,3,2,1], false) => sort([1,2,3,4,5], [], true) round 5 => sort([2,3,4,5], [1], true) => sort([3,4,5],[2,1], true) => sort([4,5],[3,2,1], true) => sort([5],[4,3,2,1], true) => sort([], [5,4,3,2,1], true) => [1,2,3,4,5]
-module(bubbleSort). -compile(export_all). -include_lib("eunit/include/eunit.hrl"). sort(L) -> sort(L, length(L), []). sort(_L, 0, _Res) -> []; sort([H | _T], 1, Res) -> [H | Res]; sort(L, Len, Res) -> T1 = lists:sublist(L, 1, Len), T2 = inner_sort(T1, []), Last = lists:last(T2), sort(T2, Len - 1, [Last | Res]). inner_sort([A, B], Res) when (A < B)-> Res ++ [A, B]; inner_sort([A, B], Res) -> Res ++ [B, A]; inner_sort([A, B | T], Res) when (A < B) -> inner_sort([B | T], Res ++ [A]); inner_sort([A, B | T], Res) -> inner_sort([A | T], Res ++ [B]). test()-> L = [5, 3, -1, 10, 6, 100, 99], ?assert(sort([]) =:= []), ?assert(sort([1]) =:= [1]), ?assert(sort([1, 2, 3, 4]) =:= [1, 2, 3, 4]), ?assert(sort([10, 5, 3, 2, 1]) =:= [1, 2, 3, 5, 10]), ?assert(sort(L) =:= [-1, 3, 5, 6, 10, 99, 100]).
Easy to read & understand implementation w ascending sort direction. Tail recursion employed, computational complexity & memory usage are classic. bsort([]) -> []; bsort([H|T]) -> bsort([H|T],[]). bsort([],[]) -> []; bsort([],[H|T]) -> [H|T]; bsort([A|B], Sorted) when is_list(Sorted) -> M = lists:max([A|B]), bsort([A|B] -- [M], [M] ++ Sorted).
Erlang - list comprehensions - populating records
I have a simple record structure consisting of a header (H) and a list of the data lines (D) 1:N. All header lines must start with a digit. All data lines have a leading whitespace. There also might be some empty lines (E) in between that must be ignored. L = [H, D, D, E, H, D, E, H, D, D, D]. I would like to create a list of records: -record(posting,{header,data}). using list comprehension. Whats the best way to do it?
You must use lists:foldl/3 instead of list comprehensions in this case. With foldl/3 you can accumulate values of header and data through whole list L.
You should do something like this: make_records(L) when is_list(L) -> F = fun([32|_]=D,{#posting{}=H,Acc}) -> {H,[H#posting{data=D}|Acc]}; ([], Acc) -> Acc; ([F|_]=H, {_,Acc}) when F=<$0, F>=$9 -> {#posting{header=>H}, Acc} end, {_, R} = lists:foldl(F, {undefined, []}, L), R. Anyway I think that straightforward Erlang version doesn't seems too complicated and should be little bit faster. make_records2(L) when is_list(L) -> make_records2(L, undefined, []). make_records2([], _, R) -> R; make_records2([[32|_]=D|T], H, Acc) when is_list(H) -> make_records2(T, H, [#posting{header=H,data=D}|Acc]); make_records2([[]|T], H, Acc) -> make_records2(T, H, Acc); make_records2([[F|_]=H|T], _, Acc) when F>=$0, F=<$9 -> make_records2(T, H, Acc). Edit: If you have to add better row classification or parsing, adding new function is better because it improves readability. parse_row([Digit|_]=R) when Digit >= $0, Digit =< $9 -> {header, R}; parse_row(R) -> try_spaces(R). try_spaces([]) -> empty; try_spaces([Sp|R]) when Sp=:=$\s; Sp=:=$\t; Sp=:=$\n -> try_spaces(R); % skip all white spaces from Data field try_spaces(Data) -> {data, Data}. You can use it like this: make_records(L) when is_list(L) -> F = fun(Row, {H, Acc}) -> case parse_row(Row) of {data, D} when is_record(H, posting) -> {H,[H#posting{data=D}|Acc]}; empty -> Acc; {header, H} -> {#posting{header=>H}, Acc} end, {_, R} = lists:foldl(F, {undefined, []}, L), R. Tail recursive native Erlang solution: make_records2(L) when is_list(L) -> make_records2([parse_row(R) || R<-L], undefined, []). make_records2([], _, R) -> R; make_records2([{data, D}|T], H, Acc) when is_list(H) -> make_records2(T, H, [#posting{header=H,data=D}|Acc]); make_records2([empty|T], H, Acc) -> make_records2(T, H, Acc); make_records2([{header,H}|T], _, Acc) -> make_records2(T, H, Acc). I think that there is no reason use tail recursion from performance point of view: make_records3(L) when is_list(L) -> make_records3(L, undefined). make_records3([], _) -> []; make_records3([R|T], H) -> case parse_row(R) of {data, D} when is_list(H) -> [#posting{head=H,data=D}|make_records3(T, H)]; empty -> make_records3(T, H); {header, H2} -> make_records3(T, H2) end. ... and many many other variants.
I needed to collapse all Data lines beneath the header - so for the moment here is what I have: sanitize(S) -> trim:trim(S). make_records(L) when is_list(L) -> make_records(L, undefined, []). make_records([], _, R) -> lists:reverse(R); make_records([[32|_]=D|T], H, Acc) when is_tuple(H) -> make_records(T, {element(1,H),[sanitize(D)|element(2,H)]},Acc); make_records([[$\n|_]=D|T], H, Acc) when is_tuple(H) -> make_records(T, H, Acc); make_records([[F|_]=H|T], B, Acc) when F>=$0, F=<$9 -> if is_tuple(B) -> make_records(T, {sanitize(H),[]}, [#posting{header=element(1,B), data=lists:reverse(element(2,B))}|Acc]); true -> make_records(T, {sanitize(H),[]}, Acc) end.