How can I sort a list from high to low
G-d willing
Hello,
How can I sort a list of Decimal values from highest to lowest without removing the duplicated values?
The sort function arranges the elements from lowest to highest and it does not get any additional parameter that defines the sorting order (ascending or descending),
I looked on the DA.List soring functions and I didn’t find anything that match
Thanks,
You can use a custom ordering with the sortBy function. For something with an existing ordering that you can use <, > etc on, you can use compare to get your hands on that ordering. So to sort descending (the default is ascending), you can define a descending ordering like this:
module Main where
import DA.Assert
import DA.List
import Daml.Script
descending : Ord a => a -> a -> Ordering
descending a b = case compare a b of
EQ -> EQ
GT -> LT
LT -> GT
t = script do
let
xs = [15,67,7,8,1,7,1]
sorted = sort xs
reversed = reverse sorted
sortedDesc = sortBy descending xs
reversed === sortedDesc
@bernhard’s answer is the most general, but there are two other approaches that can be used for this specific case. The first one is actually present in @bernhard’s code: the reverse function. You can use (reverse . sort) instead of sort to get what you want.
The other alternative is the DA.List.sortOn function. Whereas sortBy takes a a -> a -> Ordering function, i.e. a function that compares two elements, sortOn takes a (Ord k) => a -> k function, i.e. a function that takes a single element of the list and transforms it into something sortable. In this particular case, you can sort on the opposite: sortOn negate. The more general function Down reverses the ordering for any ordered data type (whereas negate only negates numbers).
module Main where
import Daml.Script
import qualified DA.List
revSort1 : (Ord a) => [a] -> [a]
revSort1 = reverse . DA.List.sort
revSort2 : (Number a, Ord a) => [a] -> [a]
revSort2 = DA.List.sortOn negate
revSort3 : (Ord a) => [a] -> [a]
revSort3 = DA.List.sortOn Down
setup : Script ()
setup = script do
let list = [1.0, 2.0, 3.0, 3.0] : [Decimal]
let revList = [3.0, 3.0, 2.0, 1.0] : [Decimal]
assert $ revSort1 list == revList
assert $ revSort2 list == revList
assert $ revSort3 list == revList
assert $ (reverse . DA.List.sort) list == revList
assert $ (DA.List.sortOn negate) list == revList
assert $ (DA.List.sortOn Down) list == revList
pure ()
I am totally embarrassed about me forgetting the reverse function. And if I was remembering it, I wouldn’t post this question in the first place.
@bernhard thanks for your answer. Regarding the ability to implement my own compare method, I know that as I did it before, However, I was more into finding an existing function that can do the descending order by one call only.
I guess calling reverse is quite clear about what the command is performing. But calling the reverse function on top of the sort function is basically 2 operations that can cost performance (depends on the list’ length).
@Gary_Verhaegen, I have to say that I have learned new things from your answer as I wasn’t aware of the negate and Down
Re: performance, in principle (I haven’t personally checked the Daml implementations), sorting is O(nlog(n)), and reversing is O(n). So doing both ends up being O((log(n)+1)n), which isn’t all that bad. Yes, technically, it will be slower, but it’s unlikely to be the bottleneck in a real application.
Regarding performance, if you want to sort quickly I recommend using DA.List.BuiltinOrder, which compares values using the built-in order from Daml LF. To sort numbers in reverse order, use sortOn negate like @Gary_Verhaegan suggested:
import qualified DA.List.BuiltinOrder
sortReverse : [Decimal] -> [Decimal]
sortReverse = DA.List.BuiltinOrder.sortOn negate
Note that in this case, sortOn Down will not work as expected, because the built-in order of Daml LF will ignore the custom order that Down tries to use.