Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# - How to sort a list by descending and then by ascending?

Tags:

sorting

f#

I have the following short code that simply counts words of a text:

[<EntryPoint>]  
let main argv =   
    let text = File.ReadAllText("gettysburg.txt").ToLower()  
    Regex.Split(text, "\W+")  
    |> Seq.groupBy id  
    |> Seq.map (fun (k, grp) -> (k, grp |> Seq.length))  
    |> Seq.sortByDescending (fun (w, ln) -> (ln, w))  
    |> Seq.iter (fun (k, ln) -> printfn "%s\t%i" k ln)  

The sort function is sorting first by the number of occurrences, and then by the word in alphabetical order, both descending. I need to do it descending by occurrences, but then ascending by alphabetical order.

With LINQ is very easy with OrderByDescending and ThenBy. How I achieve the same result in F# without recurring to LINQ?

Thank you.

like image 479
David Jiménez Martínez Avatar asked Dec 14 '22 10:12

David Jiménez Martínez


1 Answers

You can use sortBy instead and make negative the number of occurrences:

|> Seq.sortBy (fun (w, ln) -> (-ln, w))

Alternatively, if you run into more complicated cases you can use the function sortWith which uses a comparer function:

|> Seq.sortWith (fun (w1, ln1) (w2, ln2) -> compare (ln2, w1) (ln1, w2))

In fact the first solution is a short-cut for your specific case because you can make the number negative, but if you have more fields, not numeric, then by using sortWith you can achieve the same as with OrderBy ... ThenBy in Linq, by ordering the tuple and swapping the elements when it's descending.

Note that both alternatives rely on structural comparison over tuples.

like image 56
Gus Avatar answered Jan 18 '23 23:01

Gus