Purely-functional, singly-linked lists.
A list of type List<T>
is either null
or an optional pair of a value of type T
and a tail, itself of type List<T>
.
To use this library, import it using:
motoko name=initialize
import List "mo:base/List";
public func empty<T>() : List<T>
Create an empty list.
Example:
motoko include=initialize
List.empty<Nat>() // => null
Runtime: O(1)
Space: O(1)
public func isEmpty<T>(list : List<T>) : Bool
Check whether a list is empty and return true if the list is empty.
Example:
motoko include=initialize
List.isEmpty<Nat>(null) // => true
Runtime: O(1)
Space: O(1)
public func size<T>(list : List<T>) : Nat
Return the length of the list.
Example:
motoko include=initialize
List.size<Nat>(?(0, ?(1, null))) // => 2
Runtime: O(size)
Space: O(1)
public func contains<T>(
list : List<T>,
equal : (T, T) -> Bool,
item : T
) : Bool
Check whether the list contains a given value. Uses the provided equality function to compare values.
Example:
motoko include=initialize
import Nat "mo:base/Nat";
List.contains<Nat>(?(1, ?(2, ?(3, null))), Nat.equal, 2) // => true
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that equal
runs in O(1) time and space.
public func get<T>(list : List<T>, n : Nat) : ?T
Access any item in a list, zero-based.
NOTE: Indexing into a list is a linear operation, and usually an indication that a list might not be the best data structure to use.
Example:
motoko include=initialize
List.get<Nat>(?(0, ?(1, null)), 1) // => ?1
Runtime: O(size)
Space: O(1)
public func pushFront<T>(list : List<T>, item : T) : List<T>
Add item
to the head of list
, and return the new list.
Example:
motoko include=initialize
List.push<Nat>(null, 0) // => ?(0, null);
Runtime: O(1)
Space: O(1)
public func last<T>(list : List<T>) : ?T
Return the last element of the list, if present. Example:
motoko include=initialize
List.last<Nat>(?(0, ?(1, null))) // => ?1
Runtime: O(size)
Space: O(1)
public func popFront<T>(list : List<T>) : (?T, List<T>)
Remove the head of the list, returning the optioned head and the tail of the list in a pair.
Returns (null, null)
if the list is empty.
Example:
motoko include=initialize
List.pop<Nat>(?(0, ?(1, null))) // => (?0, ?(1, null))
Runtime: O(1)
Space: O(1)
public func reverse<T>(list : List<T>) : List<T>
Reverses the list.
Example:
motoko include=initialize
List.reverse<Nat>(?(0, ?(1, ?(2, null)))) // => ?(2, ?(1, ?(0, null)))
Runtime: O(size)
Space: O(size)
public func forEach<T>(list : List<T>, f : T -> ())
Call the given function for its side effect, with each list element in turn.
Example:
motoko include=initialize
var sum = 0;
List.forEach<Nat>(?(0, ?(1, ?(2, null))), func n = sum += n);
sum // => 3
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
public func map<T1, T2>(list : List<T1>, f : T1 -> T2) : List<T2>
Call the given function f
on each list element and collect the results
in a new list.
Example:
motoko include=initialize
import Nat = "mo:base/Nat"
List.map<Nat, Text>(?(0, ?(1, ?(2, null))), Nat.toText) // => ?("0", ?("1", ?("2", null))
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
public func filter<T>(list : List<T>, f : T -> Bool) : List<T>
Create a new list with only those elements of the original list for which the given function (often called the predicate) returns true.
Example:
motoko include=initialize
List.filter<Nat>(?(0, ?(1, ?(2, null))), func n = n != 1) // => ?(0, ?(2, null))
Runtime: O(size)
Space: O(size)
public func filterMap<T, R>(list : List<T>, f : T -> ?R) : List<R>
Call the given function on each list element, and collect the non-null results in a new list.
Example:
motoko include=initialize
List.filterMap<Nat, Nat>(
?(1, ?(2, ?(3, null))),
func n = if (n > 1) ?(n * 2) else null
) // => ?(4, ?(6, null))
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
public func mapResult<T, R, E>(list : List<T>, f : T -> Result.Result<R, E>) : Result.Result<List<R>, E>
Maps a Result
-returning function f
over a List
and returns either
the first error or a list of successful values.
Example:
motoko include=initialize
List.mapResult<Nat, Nat, Text>(
?(1, ?(2, ?(3, null))),
func n = if (n > 0) #ok(n * 2) else #err "Some element is zero"
) // => #ok ?(2, ?(4, ?(6, null))
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
public func partition<T>(list : List<T>, f : T -> Bool) : (List<T>, List<T>)
Create two new lists from the results of a given function (f
).
The first list only includes the elements for which the given
function f
returns true and the second list only includes
the elements for which the function returns false.
Example:
motoko include=initialize
List.partition<Nat>(?(0, ?(1, ?(2, null))), func n = n != 1) // => (?(0, ?(2, null)), ?(1, null))
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
public func concat<T>(list1 : List<T>, list2 : List<T>) : List<T>
Append the elements from one list to another list.
Example:
motoko include=initialize
List.concat<Nat>(
?(0, ?(1, ?(2, null))),
?(3, ?(4, ?(5, null)))
) // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null))))))
Runtime: O(size(l))
Space: O(size(l))
public func join<T>(iter : Iter.Iter<List<T>>) : List<T>
Flatten, or repatedly concatenate, an iterator of lists as a list.
Example:
motoko include=initialize
List.join<Nat>(
[ ?(0, ?(1, ?(2, null))),
?(3, ?(4, ?(5, null))) ] |> Iter.fromArray _)
); // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null))))))
Runtime: O(size*size)
Space: O(size*size)
public func flatten<T>(list : List<List<T>>) : List<T>
Flatten, or repatedly concatenate, a list of lists as a list.
Example:
motoko include=initialize
List.flatten<Nat>(
?(?(0, ?(1, ?(2, null))),
?(?(3, ?(4, ?(5, null))),
null))
); // => ?(0, ?(1, ?(2, ?(3, ?(4, ?(5, null))))))
Runtime: O(size*size)
Space: O(size*size)
public func take<T>(list : List<T>, n : Nat) : List<T>
Returns the first n
elements of the given list.
If the given list has fewer than n
elements, this function returns
a copy of the full input list.
Example:
motoko include=initialize
List.take<Nat>(
?(0, ?(1, ?(2, null))),
2
); // => ?(0, ?(1, null))
Runtime: O(n)
Space: O(n)
public func drop<T>(list : List<T>, n : Nat) : List<T>
Drop the first n
elements from the given list.
Example:
motoko include=initialize
List.drop<Nat>(
?(0, ?(1, ?(2, null))),
2
); // => ?(2, null)
Runtime: O(n)
Space: O(1)
public func foldLeft<T, A>(
list : List<T>,
base : A,
combine : (A, T) -> A
) : A
Collapses the elements in list
into a single value by starting with base
and progessively combining elements into base
with combine
. Iteration runs
left to right.
Example:
motoko include=initialize
import Nat "mo:base/Nat";
List.foldLeft<Nat, Text>(
?(1, ?(2, ?(3, null))),
"",
func (acc, x) = acc # Nat.toText(x)
) // => "123"
Runtime: O(size(list))
Space: O(1) heap, O(1) stack
*Runtime and space assumes that combine
runs in O(1) time and space.
public func foldRight<T, A>(
list : List<T>,
base : A,
combine : (T, A) -> A
) : A
Collapses the elements in buffer
into a single value by starting with base
and progessively combining elements into base
with combine
. Iteration runs
right to left.
Example:
motoko include=initialize
import Nat "mo:base/Nat";
List.foldRight<Nat, Text>(
?(1, ?(2, ?(3, null))),
"",
func (x, acc) = Nat.toText(x) # acc
) // => "123"
Runtime: O(size(list))
Space: O(1) heap, O(size(list)) stack
*Runtime and space assumes that combine
runs in O(1) time and space.
public func find<T>(list : List<T>, f : T -> Bool) : ?T
Return the first element for which the given predicate f
is true,
if such an element exists.
Example:
motoko include=initialize
List.find<Nat>(
?(1, ?(2, ?(3, null))),
func n = n > 1
); // => ?2
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that f
runs in O(1) time and space.
public func all<T>(list : List<T>, f : T -> Bool) : Bool
Return true if the given predicate f
is true for all list
elements.
Example:
motoko include=initialize
List.all<Nat>(
?(1, ?(2, ?(3, null))),
func n = n > 1
); // => false
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that f
runs in O(1) time and space.
public func any<T>(list : List<T>, f : T -> Bool) : Bool
Return true if there exists a list element for which
the given predicate f
is true.
Example:
motoko include=initialize
List.any<Nat>(
?(1, ?(2, ?(3, null))),
func n = n > 1
) // => true
Runtime: O(size(list))
Space: O(1)
*Runtime and space assumes that f
runs in O(1) time and space.
public func merge<T>(
list1 : List<T>,
list2 : List<T>,
compare : (T, T) -> Order.Order
) : List<T>
Merge two ordered lists into a single ordered list.
This function requires both list to be ordered as specified
by the given relation compare
.
Example:
motoko include=initialize
List.merge<Nat>(
?(1, ?(2, ?(4, null))),
?(2, ?(4, ?(6, null))),
Nat.compare
); // => ?(1, ?(2, ?(2, ?(4, ?(4, ?(6, null))))))),
Runtime: O(size(l1) + size(l2))
Space: O(size(l1) + size(l2))
*Runtime and space assumes that lessThanOrEqual
runs in O(1) time and space.
public func equal<T>(
list1 : List<T>,
list2 : List<T>,
equalItem : (T, T) -> Bool
) : Bool
Check if two lists are equal using the given equality function to compare elements.
Example:
motoko include=initialize
import Nat "mo:base/Nat";
List.equal<Nat>(?(1, ?(2, null)), ?(1, ?(2, null)), Nat.equal) // => true
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that equalItem
runs in O(1) time and space.
public func compare<T>(
list1 : List<T>,
list2 : List<T>,
compareItem : (T, T) -> Order.Order
) : Order.Order
Compare two lists using lexicographic ordering specified by argument function compareItem
.
Example:
motoko include=initialize
import Nat "mo:base/Nat";
List.compare<Nat>(
?(1, ?(2, null)),
?(3, ?(4, null)),
Nat.compare
) // => #less
Runtime: O(size(l1))
Space: O(1)
*Runtime and space assumes that argument compare
runs in O(1) time and space.
public func tabulate<T>(n : Nat, f : Nat -> T) : List<T>
Generate a list based on a length and a function that maps from a list index to a list element.
Example:
motoko include=initialize
List.tabulate<Nat>(
3,
func n = n * 2
) // => ?(0, ?(2, (?4, null)))
Runtime: O(n)
Space: O(n)
*Runtime and space assumes that f
runs in O(1) time and space.
public func singleton<T>(item : T) : List<T>
Create a list with exactly one element.
Example:
motoko include=initialize
List.singleton<Nat>(
0
) // => ?(0, null)
Runtime: O(1)
Space: O(1)
public func repeat<T>(item : T, n : Nat) : List<T>
Create a list of the given length with the same value in each position.
Example:
motoko include=initialize
List.repeat<Nat>(
3,
0
) // => ?(0, ?(0, ?(0, null)))
Runtime: O(n)
Space: O(n)
public func zip<T, U>(list1 : List<T>, list2 : List<U>) : List<(T, U)>
Create a list of pairs from a pair of lists.
If the given lists have different lengths, then the created list will have a length equal to the length of the smaller list.
Example:
motoko include=initialize
List.zip<Nat, Text>(
?(0, ?(1, ?(2, null))),
?("0", ?("1", null)),
) // => ?((0, "0"), ?((1, "1"), null))
Runtime: O(min(size(xs), size(ys)))
Space: O(min(size(xs), size(ys)))
public func zipWith<T, U, V>(
list1 : List<T>,
list2 : List<U>,
f : (T, U) -> V
) : List<V>
Create a list in which elements are created by applying function f
to each pair (x, y)
of elements
occuring at the same position in list xs
and list ys
.
If the given lists have different lengths, then the created list will have a length equal to the length of the smaller list.
Example:
motoko include=initialize
import Nat = "mo:base/Nat";
import Char = "mo:base/Char";
List.zipWith<Nat, Char, Text>(
?(0, ?(1, ?(2, null))),
?('a', ?('b', null)),
func (n, c) = Nat.toText(n) # Char.toText(c)
) // => ?("0a", ?("1b", null))
Runtime: O(min(size(xs), size(ys)))
Space: O(min(size(xs), size(ys)))
*Runtime and space assumes that f
runs in O(1) time and space.
public func split<T>(list : List<T>, n : Nat) : (List<T>, List<T>)
Split the given list at the given zero-based index.
Example:
motoko include=initialize
List.split<Nat>(
2,
?(0, ?(1, ?(2, null)))
) // => (?(0, ?(1, null)), ?(2, null))
Runtime: O(n)
Space: O(n)
public func chunks<T>(list : List<T>, n : Nat) : List<List<T>>
Split the given list into chunks of length n
.
The last chunk will be shorter if the length of the given list
does not divide by n
evenly. Traps if n
= 0.
Example:
motoko include=initialize
List.chunks<Nat>(
2,
?(0, ?(1, ?(2, ?(3, ?(4, null)))))
)
/* => ?(?(0, ?(1, null)),
?(?(2, ?(3, null)),
?(?(4, null),
null)))
*/
Runtime: O(size)
Space: O(size)
public func values<T>(list : List<T>) : Iter.Iter<T>
Returns an iterator to the elements in the list.
Example:
motoko include=initialize
var p = "";
for (e in List.values([3, 1, 4]))
p #= debug_show e;
p // => "314"
public func fromArray<T>(array : [T]) : List<T>
Convert an array into a list.
Example:
motoko include=initialize
List.fromArray<Nat>([0, 1, 2, 3, 4])
// => ?(0, ?(1, ?(2, ?(3, ?(4, null)))))
Runtime: O(size)
Space: O(size)
public func fromVarArray<T>(array : [var T]) : List<T>
Convert a mutable array into a list.
Example:
motoko include=initialize
List.fromVarArray<Nat>([var 0, 1, 2, 3, 4])
// => ?(0, ?(1, ?(2, ?(3, ?(4, null)))))
Runtime: O(size)
Space: O(size)
public func toArray<T>(list : List<T>) : [T]
Create an array from a list. Example:
motoko include=initialize
List.toArray<Nat>(?(0, ?(1, ?(2, ?(3, ?(4, null))))))
// => [0, 1, 2, 3, 4]
Runtime: O(size)
Space: O(size)
public func toVarArray<T>(list : List<T>) : [var T]
Create a mutable array from a list. Example:
motoko include=initialize
List.toVarArray<Nat>(?(0, ?(1, ?(2, ?(3, ?(4, null))))))
// => [var 0, 1, 2, 3, 4]
Runtime: O(size)
Space: O(size)
public func fromIter<T>(iter : Iter.Iter<T>) : List<T>
Turn an iterator into a list, consuming it. Example:
motoko include=initialize
List.fromIter<Nat>([0, 1, 2, 3, 4].values())
// => ?(0, ?(1, ?(2, ?(3, ?(4, null))))))
Runtime: O(size)
Space: O(size)
public func toText<T>(list : List<T>, f : T -> Text) : Text