Perform operations on lists
When you store many items in a list, you often carry out operations on either part or all of the list. The list module contains many useful operations that let you do just that.
List module functions
Besides properties, there's a list module, which is full of functions that operate on a list. The functions can perform commonly used operations, such as finding, filtering, sorting, carrying out mathematical operations, and more.
Iteration
To iterate means to go through each element in a list, from a starting point to an end point. For iteration, there are two especially interesting functions:
iter()
: This function lets you iterate over each item in a list, as shown here:let cards = [ 1 .. 5 ] List.iter(fun i -> printfn "%i" i) cards // 1 2 3 4 5
The
iter()
function takes a function. In the preceding code, you're providing an anonymous function by using thefun
keyword. This function takes a parameter that represents the current item as it's being iterated through. The following code is the equivalent of writing the following code with a loop:for i in cards do printfn "%i" i
map()
: This function is similar toiter()
, but it lets you transform what you have. Here's an example:type Person = { FirstName: string; LastName: string } let people = [ { FirstName="Albert"; LastName= "Einstein" } { FirstName="Marie"; LastName="Curie" } ] let nobelPrizeWinners = List.map (fun person -> person.FirstName + person.LastName) people printfn "%A" nobelPrizeWinners // ["Albert Einstein"; "Marie Curie"]
In the preceding code, the list of
Person
objects is transformed into a list of strings.
Filter
The filter()
function also takes a function as a parameter, but its purpose is to define what elements to keep. If the expression evaluates to true
, the element is kept. If the expression is false
, the element won't be part of the filtered list. In the following example, a list is filtered to keep only items whose value is divisible by two:
let cards = [ 1 .. 5 ]
let filteredList = List.filter(fun i-> i % 2 = 0) cards
List.iter(fun i -> printfn "item %i" i) filteredList // item 2 item 4
The filteredList
list now contains only the elements that return true
when they're evaluated by i % 2 = 0
, which is 2 and 4.
Sort
Sorting lists is something you're likely to do often. Here are three functions that you might find useful when you sort lists:
sort()
, sorts in ascending order. Here's an example:let list = [2; 1; 5; 3] let sortedList = List.sort list // 1 2 3 5
sortBy()
: With this function, the idea is to point out a key to sort by. Let's say you have a list of people, and each record has the fieldsname
andage
. You can then point out to sort byage
, for example. This function takes a function where you point out the key. Another key could be the length of a string, as in this example:let fruits = ["Banana"; "Apple"; "Pineapple"] let sortedFruits = List.sortBy (fun (fruit : string) -> fruit.Length) fruits // Apple, Banana, Pineapple
sortWith()
: With this function, you can provide a comparator function, because it might not be apparent at first which of several items should be sorted before any other item. Here's some example code:// assume a type like so type MagicCreature = { Name : string; Level: int; Attack: int } let creatures = [ { Name="Dragon"; Level=2; Attack=20 } { Name="Orc"; Level=1; Attack=5 } { Name="Demon"; Level=2; Attack=10 } ] // comparison function, -1 = less than, 1 = larger than, 0 = equal let compareCreatures c1 c2 = if c1.Level < c2.Level then -1 else if c1.Level > c2.Level then 1 else if c1.Attack < c2.Attack then -1 else if c1.Attack > c2.Attack then 1 else 0 let sorted = List.sortWith compareCreatures creatures // { Name="Orc"; Level=1; Attack=5 }, { Name="Demon"; Level=2; Attack=10 }, { Name="Dragon"; Level=2; Attack=20 }
The preceding comparison function
compareCreatures()
first tries to compare byLevel
. If the level is equal, it tries to compare by usingAttack
. It returns-1
if something is considered smaller,1
if it's larger, and0
if it's equal.
Search
Another thing you might want to do is to find a specific element. To do so, you can choose from the following functions:
find()
: This function finds the first element that matches a certain condition. To usefind()
, you need to provide a function (predicate) that expresses how to find the item. Here's an example:let list = [1; 2; 3; 4] let found = List.find( fun x -> x % 2 = 0) list // 2 - Only the first element that matches the condition is returned.
tryFind()
. This function takes a function (predicate) that tells how to find the value and a list to look into. It returns an option. Here's how you can use it:let findValue aValue aList = let found = aList |> List.tryFind(fun item -> item = aValue) match found with | Some value -> printfn "%i" value | None -> printfn "Not found" findValue 1 list // 1 findValue 5 list // Not found
In the preceding code, you send in a value to compare to your list. If it's found, it returns
Some
. If it isn't found, it returnsNone
.tryFindIndex()
. LiketryFind()
, this function returns an option, and it takes a function (predicate) that evaluates to a Boolean. Here's what the code might look like:let found = List.tryFindIndex(fun x -> x = 4) list match found with | Some index -> printfn "%i" index | None -> printfn "Not found"
Arithmetic operations
Carrying out mathematical operations on a list can be valuable. Among many functions to choose from in the List API, here are the three most useful ones:
sum()
: By using this function, you iterate over each item to sum all values in the list. Here's how you can use it:let sum = List.sum [1 .. 5] // sum = 15
sumBy()
: With this function, the idea is to point out how to sum the values. One way to do so is to point out which fields to sum, as in the following example:type OrderItem = { Name: string; Cost:int } let orderItems = [ { Name="XBox"; Cost=500 } { Name="Book"; Cost=10 } { Name="Movie ticket"; Cost=7 } ] let sum = List.sumBy(fun item -> item.Cost) orderItems printfn "%i" sum // 517
In the preceding code, the
Cost
field is pointed out, and each item in that field adds to the total.average()
: This function is similar tosum()
in that it operates on a list of numbers, but with two differences:- It expects the data to be floating point numbers, not integers.
- It calculates an average rather than a sum.
Here's an example:
let numbers = [ 1.0; 2.5; 3.0 ] let avg = List.average numbers printfn "%f" avg // 2.166667
averageBy()
: LikesumBy()
,averageBy()
takes a function where you specify what value you want. Here's an example:type WeatherMeasurement = { Date: string; Temperature: float } let measurements = [ { Date="07/20/2021"; Temperature=21.3 } { Date="07/21/2021"; Temperature=23.2 } { Date="07/22/2021"; Temperature=20.7 } ] let avgBy = List.averageBy(fun m -> m.Temperature) measurements printfn "%f" avgBy // 21.733333