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.

Resources