Sort list of list erlang - erlang

PropertyInfo = [
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
[{LandNo, Acquisition, Heir, Property, LandTypeCount, LandType}],
]
PropertyInfo is a list of lists containing database objects in tuple, where Heir:code() will return a 6-digit code eg. "010011", "00209", ""020011".
How can I sort this list in erlang by using that Heir code?

By using lists:sort/2 and an ordering function:
manual excerpt:
sort(Fun, List1) -> List2
Types:
Fun = fun((A :: T, B :: T) -> boolean())
List1 = List2 = [T]
T => term()
Returns a list containing the sorted elements of List1,
according to the ordering function Fun. Fun(A, B) should return true
if A compares less than or equal to B in the ordering, false
otherwise.
Ordering fun could look like this:
fun([Tuple1],[Tuple2]) ->
Prop1 = element(3,Tuple1);
Prop2 = element(3,Tuple2);
case {Prop1:code(),Prop2:code()} of
{Same,Same} -> true;
{Code1,Code2} -> SomeComparisonFun(Code1,Code2)
end
end
This leaves you to provide a function that can compare those values, once you've decided on a metric that let's you say which value should be greater than some other one.

SortedList = lists:sort(
fun({_, _, A, _, _, _}, {_, _, B, _, _, _}) ->
A:code() =< B:code()
end,
PropertyInfo).
This was very simple, I used this inbuilt lists:sort() function.

Related

F# Change element in list and return full new list

I have a list of type (string * (int * int)) list. I want to be able to search through the list, finding the right element by it's string identifier, do a calculation on one of the ints, and then return the full, modified list.
Example:
Given a list
let st = [("a1",(100,10)); ("a2",(50,20)); ("a3",(25,40))]
I'm trying to make a function which gets one of the elements and subtracts number from one of the ints in the tuple.
get ("a2",10) st
//Expected result: st' = [("a1",(100,10)); ("a2",(40,20)); ("a3",(25,40))]
I feel I'm almost there, but am a little stuck with the following function:
let rec get (a,k) st =
match st with
| (a',(n',p'))::rest when a'=a && k<=n' -> (n'-k,p')::rest
| (a',(n',p'))::rest -> (n',p')::get (a,k) rest
| _ -> failwith "Illegal input"
This returns [("a2",(40,20)); ("a3",(25,40))] and is thus missing the first a1 element. Any hints?
Lists are immutable, so if you want to "change one element" you are really creating a new list with one element transformed. The easiest way to do a transformation like this is to use List.map function. I would write something like:
let updateElement key f st =
st |> List.map (fun (k, v) -> if k = key then k, f v else k, v)
updateElement is a helper that takes a key, update function and an input. It returns list where the element with the given key has been transformed using the given function. For example, to increment the first number associated with a2, you can write:
let st = [("a1",(100,10)); ("a2",(50,20)); ("a3",(25,40))]
st |> updateElement "a2" (fun (a, b) -> a + 10, b)
I was looking for a function which would update an element in a list based on the element's data. I couldn't find one in F#5, so wrote one using Tomas' solution:
let updateAt (elemFindFunc: 'a -> bool) (newElem: 'a) (source: 'a list) : 'a list =
source
|> List.map
(fun elem ->
let foundElem = elemFindFunc elem
if foundElem then newElem else elem)
elemFindFunc is the function which consumes an element and returns true if this is the element we want to replace. If this function returns true for multiple elements, then those will be replaced by newElem. Also, if elemFindFunc evaluates to false for all elements, the list will be unaltered.
newElem is the new value you want to replace with. newElem could be replaced by a function like valueFunc: 'a -> 'a if you want to process the element before inserting it.

Function string => record member in erlang

I'm wondering how can i define function, which as an argument takes a string and returns a member of the record.
For example with the record
-record(measurement, { temperature, pm2p5, pm10, pressure, humidity, others=[]}).
And the fragment of my function:
update_measurement(Measurement, Type_as_String, Value) ->
Measurement#measurement{get_type(Type_as_String) = Value}
I want to update a value by passing a type as string, and I don't have an idea to define the function get_type(Type_as_String).
I've tried with atoms, but it didn't work.
Something like
update_measurement(Measurement, Type_as_String, Value) ->
case Type_as_String of
"temperature" -> Measurement#measurement{temperature = Value};
"humidity" -> Measurement#measurement{humidity = Value};
...
isn't ok, because i want to reuse this pattern in other functions.
If performance is not your biggest concern:
update_measurement(Measurement, Type_as_String, Value) ->
update_field(Measurement, Type_as_String, Value).
update_field(#measurement{} = Record, SKey, Value) ->
update_field(Record, SKey, Value, record_info(fields, measurement));
% add other records here
update_field(_, _, _) -> error(bad_record).
update_field(Record, SKey, Value, Fields) ->
update_field(Record, list_to_existing_atom(SKey), Value, Fields, 2).
update_field(Record, Key, Value, [Key|_], N) ->
setelement(N, Record, Value);
update_field(Record, Key, Value, [_|Fields], N) ->
update_field(Record, Key, Value, Fields, N+1);
update_field(_, _, _, [], _) ->
error(bad_key).
Note record_info/2 is not a real function but you have to provide measurement as a compile time constant.

How can I determine if a list of discriminated union types are of the same case?

Suppose I have a DU like so:
type DU = Number of int | Word of string
And suppose I create a list of them:
[Number(1); Word("abc"); Number(2)]
How can I write a function that would return true for a list of DUs where all the elements are the same case. For the above list it should return false.
The general approach I'd use here would be to map the union values into tags identifying the cases, and then check if the resulting set of tags has at most one element.
let allTheSameCase (tagger: 'a -> int) (coll: #seq<'a>) =
let cases =
coll
|> Seq.map tagger
|> Set.ofSeq
Set.count cases <= 1
For the tagger function, you can assign the tags by hand:
allTheSameCase (function Number _ -> 0 | Word _ -> 1) lst
or use reflection (note that you might need to set binding flags as necessary):
open Microsoft.FSharp.Reflection
let reflectionTagger (case: obj) =
let typ = case.GetType()
if FSharpType.IsUnion(typ)
then
let info, _ = FSharpValue.GetUnionFields(case, typ)
info.Tag
else -1 // or fail, depending what makes sense in the context.
In case you wanted to check that the elements of a list are of a specific union case, it's straightforward to provide a predicate function.
let isNumbers = List.forall (function Number _ -> true | _ -> false)
If you do not care which union case, as long as they are all the same, you need to spell them all out explicitly. Barring reflection magic to get a property not exposed inside F#, you also need to assign some value to each case. To avoid having to think up arbitrary values, we can employ an active pattern which maps to a different DU behind the scenes.
let (|IsNumber|IsWord|) = function
| Number _ -> IsNumber
| Word _ -> IsWord
let isSameCase src =
src |> Seq.groupBy (|IsNumber|IsWord|) |> Seq.length <= 1
I had the exact same use case recently and the solution can be done much simpler than complicated reflections or explicit pattern matching, GetType does all the magic:
let AreAllElementsOfTheSameType seq = // seq<'a> -> bool
if Seq.isEmpty seq then true else
let t = (Seq.head seq).GetType ()
seq |> Seq.forall (fun e -> (e.GetType ()) = t)

Find the minimum value in a map

I have a map organized as follows.Key is a simple term lets say an integer but the value is complex tuple {BB,CC,DD}. What is the best way to find the minimum CC in the map ? So far I have the following
-module(test).
-author("andre").
%% API
-export([init/0]).
init() ->
TheMap = build(maps:new(), 20),
io:format("Map: ~p~n", [TheMap]),
AKey = hd(maps:keys(TheMap)),
AValue = maps:get(AKey, TheMap),
maps:fold(fun my_min/3, {AKey, AValue}, TheMap).
build(MyMap, Count) when Count == 0 ->
MyMap;
build(MyMap, Count) ->
NewMap = maps:put(Count, {random:uniform(100), random:uniform(100), random:uniform(100)}, MyMap),
build(NewMap, Count - 1).
my_min(Key, {A,B,C}, {MinKey, {AA,BB,CC}}) ->
if B < BB -> {Key, {A,B,C}};
B >= BB -> {MinKey, {AA,BB,CC}}
end.
My map is small so I am not too worried about the usage of AKey and AValue to find initial values for the fold, but I was wondering if there was a better way, or other data structure.
--
Thanks.
What you have is close to a good solution, but it can be improved. There's no need to dig out the first key and value to use an the initial value for the fold, since you can just pass an artificial value instead and make your fold function deal with it. Also, you can improve your use of pattern matching in function heads. Lastly, use start instead of init since that makes it easier to invoke when calling erl from the command line.
Here's an improved version:
-module(test).
-author("andre").
%% API
-export([start/0]).
start() ->
TheMap = build(maps:new(), 20),
io:format("Map: ~p~n", [TheMap]),
maps:fold(fun my_min/3, {undefined, undefined}, TheMap).
build(MyMap, 0) ->
MyMap;
build(MyMap, Count) ->
NewMap = maps:put(Count, {random:uniform(100), random:uniform(100), random:uniform(100)}, MyMap),
build(NewMap, Count - 1).
my_min(Key, Value, {undefined, undefined}) ->
{Key, Value};
my_min(Key, {_,B,_}=Value, {_, {_,BB,_}}) when B < BB ->
{Key, Value};
my_min(_Key, _Value, Acc) ->
Acc.
The my_min/3 fold function has three clauses. The first matches the special start value {undefined, undefined} and returns as the new accumulator value whatever {Key, Value} it was passed. The benefit of this is not only that you avoid special processing before starting the fold, but also that if the map is empty, you'll get the special value {undefined, undefined} as the result and you can handle it accordingly. The second clause uses a guard to check if B of the value is less than the BB value in the fold accumulator, and if it is, return {Key, Value} as the new accumulator value. The final clause just returns the existing accumulator value, since this clause is called only for values greater than or equal to that in the existing accumulator.
You might also look into using a simple list of key/value tuples, since for a small number of elements it might outperform a map. If your measurements indicate you should use a list, a similar fold would work for it as well.
-module(test).
-author("andre").
%% API
-export([init/0]).
init() ->
TheMap = build(maps:new(), 24),
io:format("Map: ~p~n", [TheMap]),
List = maps:to_list(TheMap),
io:format("List: ~p~n", [List]),
Fun = fun({_, {_, V1, _}} = Element, {_, {_, V2, _}}) when V1 < V2 ->
Element;
(_, Res) ->
Res
end,
Res = lists:foldl(Fun, hd(List), tl(List)),
io:format("Res: ~p~n", [Res]).
build(MyMap, Count) when Count == 0 ->
MyMap;
build(MyMap, Count) ->
NewMap = maps:put(Count, {random:uniform(100), random:uniform(100), random:uniform(100)}, MyMap),
build(NewMap, Count - 1).
You can use maps:to_list/1 to convert the map to a list, then you can use lists:foldl/3 to calculate the minimun value.

An element in a tuple or not in erlang?

I'd like to know if there is a function in Erlang can help me know whether an element is in a tuple or not. Like sets:is_element/2.
Tuple = {aaa,bbb,ccc}.
is_element_of_tuple(Tuple, aaa) % => true
is_element_of_tuple(Tuple, ddd) % => false
You can always transform the tuple to a list using tuple_to_list/1:
is_element_of_tuple(Tuple, Element) ->
lists:member(Element, tuple_to_list(Tuple)).
The simple answer is: no there is no function to do this. You have to write your own loop which traverses all the elements of a tuple until it either finds or does not find it. You an either convert the tuple to a list as above or write your own loop, something like:
is_element_of_tuple(E, Tuple) ->
is_element_of_tuple(E, Tuple, 1, tuple_size(Tuple)).
is_element_of_tuple(E, T, I, S) when I =< S ->
case element(I, T) of
E -> true;
_ -> is_element_of_tuple(E, T, I+1, S)
end;
is_element_of_tuple(_, _, _, _) -> false. %Done all the elements
Using a case and matching in this way means we check for exact equality, and it is probably a little faster than using =:= and checking if that returns true or false.

Resources