You don’t have to program for very long before you need to sort a list of things. F#’s collections modules are nice enough to give you a canned sort
function.
Maybe you have a list of words, and you want to sort them:
let xs = [ "To"; "be"; "or"; "not"; "to"; "be"; "That"; "is"; "the"; "question" ] let sorted = List.sort xs // val sorted : string list = // ["That"; "To"; "be"; "be"; "is"; "not"; // "or"; "question"; "the"; "to"]
Easy enough. But isn’t that interesting? ‘T’ words come before ‘B’ words. Or rather, more correctly, ‘T’ words come before ‘b’ words because, by default, all capital letters are sorted before lowercase letters. And that’s all that sort
does for you: It sorts using the default sorting mechanism.
Wouldn’t it be great if you could alter what sort
uses to sort by? Well, you can: Use sortBy
.
Here’s how to sort this list of words the way your 2nd-grade teacher taught you, that is, without respect to case. Pass sortBy
a lambda that returns the lowercase version of each string:
let sortedCaseInsensitive = xs |> List.sortBy (fun x -> x.ToLower()) // val sortedCaseInsensitive : string list = // ["be"; "be"; "is"; "not"; "or"; // "question"; "That"; "the"; "To"; "to"]
Or maybe you need to sort a list of words in order of length. Employ a lambda that returns the length of each word:
let sortedByLength = xs |> List.sortBy (fun x -> x.Length) // val sortedByLength : string list = // ["To"; "be"; "or"; "to"; "be"; "is"; // "not"; "the"; "That"; "question"]
The sortBy
function also works really well for classes and record types. You can use sortBy
to specify which member or combination of members to sort by.
Maybe you have a record type for album tracks. It has two members: track length (in minutes) and track title.
type Track = { Length : float; Name : string }
Let’s say that you have the tracks of Spock’s Beard’s V:
let ts = [ { Length = 16.467; Name = "At the End of the Day" }; { Length = 6.083; Name = "Revelation" }; { Length = 4.65; Name = "Thoughts (Part II)" }; { Length = 4.067; Name = "All on a Sunday" }; { Length = 4.65; Name = "Goodbye to Yesterday" }; { Length = 27.03; Name = "The Great Nothing" } ]
Sort the tracks by track length by passing a lambda to sortBy
that returns the length of each track:
let sortedByTrackLength = ts |> List.sortBy (fun t -> t.Length) // val sortedByTrackLength : Track list = // [{Length = 4.067; // Name = "All on a Sunday";}; // {Length = 4.65; // Name = "Thoughts (Part II)";}; // {Length = 4.65; // Name = "Goodbye to Yesterday";}; // {Length = 6.083; // Name = "Revelation";}; // {Length = 16.467; // Name = "At the End of the Day";}; // {Length = 27.03; // Name = "The Great Nothing";}]
To sort according to track name instead, use a lambda that returns the track name rather than track length:
let sortedByTrackName = ts |> List.sortBy (fun t -> t.Name) // val sortedByTrackName : Track list = // [{Length = 4.067; // Name = "All on a Sunday";}; // {Length = 16.467; // Name = "At the End of the Day";}; // {Length = 4.65; // Name = "Goodbye to Yesterday";}; // {Length = 6.083; // Name = "Revelation";}; // {Length = 27.03; // Name = "The Great Nothing";}; // {Length = 4.65; // Name = "Thoughts (Part II)";}]
One reply on “F# Friday – The sort and sortBy Functions”
[…] F# Friday – The sort and sortBy Functions – Brad Collins […]