I have a record in erlang:
-record(myrec,
{
id = 0,
price = 0,
quantity = 0
}).
I then have a list of records that I want to sort by id and price, both in descending and ascending order, where price is the first key and if two records have the same price I want to sort those by id.
How can I define a fun for this?
I'm a newb at Erlang :)
thanks, nisbus
You can use lists:split/2 for this: divide(L, N) -> divide(L, N, []).
You can use length() to find the length of a list, and can use list comprehensions to filter your list. num(L) -> length([X || X <- L, X < 1]).
Erlang has the extra facility to create records. These records consist of fields. For example, you can define a personal record which has 2 fields, one is the id and the other is the name field. In Erlang, you can then create various instances of this record to define multiple people with various names and id's.
This is a shorter solution than what has been suggested so far. First define your record:
1> rd(myrec, {id=0, price=0, quantity=0}).
myrec
Then let's invent 3 of them:
2> A = #myrec{id=1, price=10, quantity=2}, B = #myrec{id=2, price=4, quantity=3}, C = #myrec{id=3, price=10, quantity=1}.
#myrec{id = 3,price = 10,quantity = 1
Now we need a comparison function. This is where the solution is shorter. Erlang can compare terms of a tuple in the order they appear, so if we want to sort by price, then by id, we just have to compare two tuples of the form {PriceA, IdA} < {PriceB, IdB}
:
3> F = fun(X, Y) -> {X#myrec.price, X#myrec.id} < {Y#myrec.price, Y#myrec.id} end.
#Fun<erl_eval.12.113037538>
And plug it in lists:sort/2
:
4> lists:sort(F, [C,B,A]).
[#myrec{id = 2,price = 4,quantity = 3},
#myrec{id = 1,price = 10,quantity = 2},
#myrec{id = 3,price = 10,quantity = 1}]
The order is now [B, A, C]
and your list is sorted.
Note that if you wanted to sort by descending id instead, You could trick it by reversing the ids in the tuples as follows:
5> G = fun(X, Y) -> {X#myrec.price, Y#myrec.id} < {Y#myrec.price, X#myrec.id} end.
#Fun<erl_eval.12.113037538>
6> lists:sort(G, [C,B,A]).
[#myrec{id = 2,price = 4,quantity = 3},
#myrec{id = 3,price = 10,quantity = 1},
#myrec{id = 1,price = 10,quantity = 2}]
Giving us [B, C, A]
. This is not obvious to the reader, so you'd better document it or use Dustin's solution in this case. The advantage of the solution presented here is that there is no nesting required. By setting elements in either tuple in the comparison, you can pretty much compare as many of them as you want without making the code that much longer.
First, you figure out how to compare your records:
-spec compare(#myrec{}, #myrec{}) -> boolean().
compare(A, B) ->
case A#myrec.price == B#myrec.price of
true ->
A#myrec.id < B#myrec.id;
_ ->
B#myrec.price < A#myrec.price
end.
Then, you just use the normal lists:sort
function with your comparison function to get what you want (this is an eunit
test of the above I ran to make sure I did something that made sense):
compare_test() ->
R1 = #myrec{id=5, price=3, quantity=2},
R2 = #myrec{id=6, price=5, quantity=1},
R3 = #myrec{id=7, price=5, quantity=0},
false = compare(R1, R2),
true = compare(R2, R1),
true = compare(R2, R3),
false = compare(R3, R2),
false = compare(R1, R3),
true = compare(R3, R1),
% Run a sort with the above comparator.
[R2, R3, R1] = lists:sort(fun compare/2, [R1, R2, R3]).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With