core/Array
Provides extended utility functions on immutable Arrays (values of type [T]
).
Note the difference between mutable ([var T]
) and immutable ([T]
) arrays.
Mutable arrays allow their elements to be modified after creation, while
immutable arrays are fixed once created.
WARNING: If you are looking for a list that can grow and shrink in size,
it is recommended you use List
for those purposes.
Arrays must be created with a fixed size.
Import from the core library to use this module.
import Array "mo:core/Array";
Function empty
func empty<T>() : [T]
Creates an empty array (equivalent to []
).
let array = Array.empty<Text>();
assert array == [];
Runtime: O(1)
Space: O(1)
Function repeat
func repeat<T>(item : T, size : Nat) : [T]
Creates an array containing item
repeated size
times.
let array = Array.repeat<Text>("Echo", 3);
assert array == ["Echo", "Echo", "Echo"];
Runtime: O(size)
Space: O(size)
Function tabulate
func tabulate<T>(size : Nat, generator : Nat -> T) : [T]
Creates an immutable array of size size
. Each element at index i
is created by applying generator
to i.
let array : [Nat] = Array.tabulate<Nat>(4, func i = i * 2);
assert array == [0, 2, 4, 6];
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that generator
runs in O(1) time and space.
Function fromVarArray
func fromVarArray<T>(varArray : [var T]) : [T]
Transforms a mutable array into an immutable array.
let varArray = [var 0, 1, 2];
varArray[2] := 3;
let array = Array.fromVarArray<Nat>(varArray);
assert array == [0, 1, 3];
Runtime: O(size)
Space: O(1)
Function toVarArray
func toVarArray<T>(array : [T]) : [var T]
Transforms an immutable array into a mutable array.
import VarArray "mo:core/VarArray";
import Nat "mo:core/Nat";
let array = [0, 1, 2];
let varArray = Array.toVarArray<Nat>(array);
varArray[2] := 3;
assert VarArray.equal(varArray, [var 0, 1, 3], Nat.equal);
Runtime: O(size)
Space: O(1)
Function equal
func equal<T>(array1 : [T], array2 : [T], equal : (T, T) -> Bool) : Bool
Tests if two arrays contain equal values (i.e. they represent the same
list of elements). Uses equal
to compare elements in the arrays.
// Use the equal function from the Nat module to compare Nats
import {equal} "mo:core/Nat";
let array1 = [0, 1, 2, 3];
let array2 = [0, 1, 2, 3];
assert Array.equal(array1, array2, equal);
Runtime: O(size1 + size2)
Space: O(1)
*Runtime and space assumes that equal
runs in O(1) time and space.
Function find
func find<T>(array : [T], predicate : T -> Bool) : ?T
Returns the first value in array
for which predicate
returns true.
If no element satisfies the predicate, returns null.
let array = [1, 9, 4, 8];
let found = Array.find<Nat>(array, func x = x > 8);
assert found == ?9;
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that predicate
runs in O(1) time and space.
Function findIndex
func findIndex<T>(array : [T], predicate : T -> Bool) : ?Nat
Returns the first index in array
for which predicate
returns true.
If no element satisfies the predicate, returns null.
let array = ['A', 'B', 'C', 'D'];
let found = Array.findIndex<Char>(array, func(x) { x == 'C' });
assert found == ?2;
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that predicate
runs in O(1) time and space.
Function concat
func concat<T>(array1 : [T], array2 : [T]) : [T]
Create a new array by concatenating the values of array1
and array2
.
Note that Array.concat
copies its arguments and has linear complexity.
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
let result = Array.concat<Nat>(array1, array2);
assert result == [1, 2, 3, 4, 5, 6];
Runtime: O(size1 + size2)
Space: O(size1 + size2)
Function sort
func sort<T>(array : [T], compare : (T, T) -> Order.Order) : [T]
Sorts the elements in the array according to compare
.
Sort is deterministic and stable.
import Nat "mo:core/Nat";
let array = [4, 2, 6];
let sorted = Array.sort(array, Nat.compare);
assert sorted == [2, 4, 6];
Runtime: O(size * log(size))
Space: O(size)
*Runtime and space assumes that compare
runs in O(1) time and space.
Function reverse
func reverse<T>(array : [T]) : [T]
Creates a new array by reversing the order of elements in array
.
let array = [10, 11, 12];
let reversed = Array.reverse(array);
assert reversed == [12, 11, 10];
Runtime: O(size)
Space: O(1)
Function forEach
func forEach<T>(array : [T], f : T -> ())
Calls f
with each element in array
.
Retains original ordering of elements.
var sum = 0;
let array = [0, 1, 2, 3];
Array.forEach<Nat>(array, func(x) {
sum += x;
});
assert sum == 6;
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
Function map
func map<T, R>(array : [T], f : T -> R) : [R]
Creates a new array by applying f
to each element in array
. f
"maps"
each element it is applied to of type X
to an element of type Y
.
Retains original ordering of elements.
let array1 = [0, 1, 2, 3];
let array2 = Array.map<Nat, Nat>(array1, func x = x * 2);
assert array2 == [0, 2, 4, 6];
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
Function filter
func filter<T>(array : [T], f : T -> Bool) : [T]
Creates a new array by applying predicate
to every element
in array
, retaining the elements for which predicate
returns true.
let array = [4, 2, 6, 1, 5];
let evenElements = Array.filter<Nat>(array, func x = x % 2 == 0);
assert evenElements == [4, 2, 6];
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that predicate
runs in O(1) time and space.
Function filterMap
func filterMap<T, R>(array : [T], f : T -> ?R) : [R]
Creates a new array by applying f
to each element in array
,
and keeping all non-null elements. The ordering is retained.
import {toText} "mo:core/Nat";
let array = [4, 2, 0, 1];
let newArray =
Array.filterMap<Nat, Text>( // mapping from Nat to Text values
array,
func x = if (x == 0) { null } else { ?toText(100 / x) } // can't divide by 0, so return null
);
assert newArray == ["25", "50", "100"];
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
Function mapResult
func mapResult<T, R, E>(array : [T], f : T -> Result.Result<R, E>) : Result.Result<[R], E>
Creates a new array by applying f
to each element in array
.
If any invocation of f
produces an #err
, returns an #err
. Otherwise
returns an #ok
containing the new array.
let array = [4, 3, 2, 1, 0];
// divide 100 by every element in the array
let result = Array.mapResult<Nat, Nat, Text>(array, func x {
if (x > 0) {
#ok(100 / x)
} else {
#err "Cannot divide by zero"
}
});
assert result == #err "Cannot divide by zero";
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
Function mapEntries
func mapEntries<T, R>(array : [T], f : (T, Nat) -> R) : [R]
Creates a new array by applying f
to each element in array
and its index.
Retains original ordering of elements.
let array = [10, 10, 10, 10];
let newArray = Array.mapEntries<Nat, Nat>(array, func (x, i) = i * x);
assert newArray == [0, 10, 20, 30];
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
Function flatMap
func flatMap<T, R>(array : [T], k : T -> Types.Iter<R>) : [R]
Creates a new array by applying k
to each element in array
,
and concatenating the resulting arrays in order.
let array = [1, 2, 3, 4];
let newArray = Array.flatMap<Nat, Int>(array, func x = [x, -x].values());
assert newArray == [1, -1, 2, -2, 3, -3, 4, -4];
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that k
runs in O(1) time and space.
Function foldLeft
func foldLeft<T, A>(array : [T], base : A, combine : (A, T) -> A) : A
Collapses the elements in array
into a single value by starting with base
and progessively combining elements into base
with combine
. Iteration runs
left to right.
import {add} "mo:core/Nat";
let array = [4, 2, 0, 1];
let sum =
Array.foldLeft<Nat, Nat>(
array,
0, // start the sum at 0
func(sumSoFar, x) = sumSoFar + x // this entire function can be replaced with `add`!
);
assert sum == 7;
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that combine
runs in O(1) time and space.
Function foldRight
func foldRight<T, A>(array : [T], base : A, combine : (T, A) -> A) : A
Collapses the elements in array
into a single value by starting with base
and progessively combining elements into base
with combine
. Iteration runs
right to left.
import {toText} "mo:core/Nat";
let array = [1, 9, 4, 8];
let bookTitle = Array.foldRight<Nat, Text>(array, "", func(x, acc) = toText(x) # acc);
assert bookTitle == "1948";
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that combine
runs in O(1) time and space.
Function join
func join<T>(arrays : Types.Iter<[T]>) : [T]
Combines an iterator of arrays into a single array. Retains the original ordering of the elements.
Consider using Array.flatten()
for better performance.
let arrays = [[0, 1, 2], [2, 3], [], [4]];
let joinedArray = Array.join<Nat>(arrays.values());
assert joinedArray == [0, 1, 2, 2, 3, 4];
Runtime: O(number of elements in array)
Space: O(number of elements in array)
Function flatten
func flatten<T>(arrays : [[T]]) : [T]
Combines an array of arrays into a single array. Retains the original ordering of the elements.
This has better performance compared to Array.join()
.
let arrays = [[0, 1, 2], [2, 3], [], [4]];
let flatArray = Array.flatten<Nat>(arrays);
assert flatArray == [0, 1, 2, 2, 3, 4];
Runtime: O(number of elements in array)
Space: O(number of elements in array)
Function singleton
func singleton<T>(element : T) : [T]
Create an array containing a single value.
let array = Array.singleton(2);
assert array == [2];
Runtime: O(1)
Space: O(1)
Function size
func size<T>(array : [T]) : Nat
Returns the size of an array. Equivalent to array.size()
.
Function isEmpty
func isEmpty<T>(array : [T]) : Bool
Returns whether an array is empty, i.e. contains zero elements.
Function fromIter
func fromIter<T>(iter : Types.Iter<T>) : [T]
Converts an iterator to an array.
Function keys
func keys<T>(array : [T]) : Types.Iter<Nat>
Returns an iterator (Iter
) over the indices of array
.
An iterator provides a single method next()
, which returns
indices in order, or null
when out of index to iterate over.
Note: You can also use array.keys()
instead of this function. See example
below.
let array = [10, 11, 12];
var sum = 0;
for (element in array.keys()) {
sum += element;
};
assert sum == 3; // 0 + 1 + 2
Runtime: O(1)
Space: O(1)
Function values
func values<T>(array : [T]) : Types.Iter<T>
Iterator provides a single method next()
, which returns
elements in order, or null
when out of elements to iterate over.
Note: You can also use array.values()
instead of this function. See example
below.
let array = [10, 11, 12];
var sum = 0;
for (element in array.values()) {
sum += element;
};
assert sum == 33;
Runtime: O(1)
Space: O(1)
Function enumerate
func enumerate<T>(array : [T]) : Types.Iter<(Nat, T)>
Iterator provides a single method next()
, which returns
pairs of (index, element) in order, or null
when out of elements to iterate over.
let array = [10, 11, 12];
var sum = 0;
for ((index, element) in Array.enumerate(array)) {
sum += element;
};
assert sum == 33;
Runtime: O(1)
Space: O(1)
Function all
func all<T>(array : [T], predicate : T -> Bool) : Bool
Returns true if all elements in array
satisfy the predicate function.
let array = [1, 2, 3, 4];
assert Array.all<Nat>(array, func x = x > 0);
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that predicate
runs in O(1) time and space.
Function any
func any<T>(array : [T], predicate : T -> Bool) : Bool
Returns true if any element in array
satisfies the predicate function.
let array = [1, 2, 3, 4];
assert Array.any<Nat>(array, func x = x > 3);
Runtime: O(size)
Space: O(1)
*Runtime and space assumes that predicate
runs in O(1) time and space.
Function indexOf
func indexOf<T>(element : T, array : [T], equal : (T, T) -> Bool) : ?Nat
Returns the index of the first element
in the array
.
import Char "mo:core/Char";
let array = ['c', 'o', 'f', 'f', 'e', 'e'];
assert Array.indexOf<Char>('c', array, Char.equal) == ?0;
assert Array.indexOf<Char>('f', array, Char.equal) == ?2;
assert Array.indexOf<Char>('g', array, Char.equal) == null;
Runtime: O(array.size())
Space: O(1)
Function nextIndexOf
func nextIndexOf<T>(element : T, array : [T], fromInclusive : Nat, equal : (T, T) -> Bool) : ?Nat
Returns the index of the next occurence of element
in the array
starting from the from
index (inclusive).
import Char "mo:core/Char";
let array = ['c', 'o', 'f', 'f', 'e', 'e'];
assert Array.nextIndexOf<Char>('c', array, 0, Char.equal) == ?0;
assert Array.nextIndexOf<Char>('f', array, 0, Char.equal) == ?2;
assert Array.nextIndexOf<Char>('f', array, 2, Char.equal) == ?2;
assert Array.nextIndexOf<Char>('f', array, 3, Char.equal) == ?3;
assert Array.nextIndexOf<Char>('f', array, 4, Char.equal) == null;
Runtime: O(array.size())
Space: O(1)
Function lastIndexOf
func lastIndexOf<T>(element : T, array : [T], equal : (T, T) -> Bool) : ?Nat
Returns the index of the last element
in the array
.
import Char "mo:core/Char";
let array = ['c', 'o', 'f', 'f', 'e', 'e'];
assert Array.lastIndexOf<Char>('c', array, Char.equal) == ?0;
assert Array.lastIndexOf<Char>('f', array, Char.equal) == ?3;
assert Array.lastIndexOf<Char>('e', array, Char.equal) == ?5;
assert Array.lastIndexOf<Char>('g', array, Char.equal) == null;
Runtime: O(array.size())
Space: O(1)
Function prevIndexOf
func prevIndexOf<T>(element : T, array : [T], fromExclusive : Nat, equal : (T, T) -> Bool) : ?Nat
Returns the index of the previous occurence of element
in the array
starting from the from
index (exclusive).
Negative indices are relative to the end of the array. For example, -1
corresponds to the last element in the array.
If the indices are out of bounds, they are clamped to the array bounds. If the first index is greater than the second, the function returns an empty iterator.
import Char "mo:core/Char";
let array = ['c', 'o', 'f', 'f', 'e', 'e'];
assert Array.prevIndexOf<Char>('c', array, array.size(), Char.equal) == ?0;
assert Array.prevIndexOf<Char>('e', array, array.size(), Char.equal) == ?5;
assert Array.prevIndexOf<Char>('e', array, 5, Char.equal) == ?4;
assert Array.prevIndexOf<Char>('e', array, 4, Char.equal) == null;
Runtime: O(array.size()); Space: O(1);
Function range
func range<T>(array : [T], fromInclusive : Int, toExclusive : Int) : Types.Iter<T>
Returns an iterator over a slice of array
starting at fromInclusive
up to (but not including) toExclusive
.
Negative indices are relative to the end of the array. For example, -1
corresponds to the last element in the array.
If the indices are out of bounds, they are clamped to the array bounds. If the first index is greater than the second, the function returns an empty iterator.
let array = [1, 2, 3, 4, 5];
let iter1 = Array.range<Nat>(array, 3, array.size());
assert iter1.next() == ?4;
assert iter1.next() == ?5;
assert iter1.next() == null;
let iter2 = Array.range<Nat>(array, 3, -1);
assert iter2.next() == ?4;
assert iter2.next() == null;
let iter3 = Array.range<Nat>(array, 0, 0);
assert iter3.next() == null;
Runtime: O(1)
Space: O(1)
Function sliceToArray
func sliceToArray<T>(array : [T], fromInclusive : Int, toExclusive : Int) : [T]
Returns a new array containing elements from array
starting at index fromInclusive
up to (but not including) index toExclusive
.
If the indices are out of bounds, they are clamped to the array bounds.
let array = [1, 2, 3, 4, 5];
let slice1 = Array.sliceToArray<Nat>(array, 1, 4);
assert slice1 == [2, 3, 4];
let slice2 = Array.sliceToArray<Nat>(array, 1, -1);
assert slice2 == [2, 3, 4];
Runtime: O(toExclusive - fromInclusive)
Space: O(toExclusive - fromInclusive)
Function toText
func toText<T>(array : [T], f : T -> Text) : Text
Converts the array to its textual representation using f
to convert each element to Text
.
import Nat "mo:core/Nat";
let array = [1, 2, 3];
let text = Array.toText<Nat>(array, Nat.toText);
assert text == "[1, 2, 3]";
Runtime: O(size)
Space: O(size)
*Runtime and space assumes that f
runs in O(1) time and space.
Function compare
func compare<T>(array1 : [T], array2 : [T], compare : (T, T) -> Order.Order) : Order.Order
Compares two arrays using the provided comparison function for elements.
Returns #less, #equal, or #greater if array1
is less than, equal to,
or greater than array2
respectively.
If arrays have different sizes but all elements up to the shorter length are equal, the shorter array is considered #less than the longer array.
import Nat "mo:core/Nat";
let array1 = [1, 2, 3];
let array2 = [1, 2, 4];
assert Array.compare<Nat>(array1, array2, Nat.compare) == #less;
import Nat "mo:core/Nat";
let array3 = [1, 2];
let array4 = [1, 2, 3];
assert Array.compare<Nat>(array3, array4, Nat.compare) == #less;
Runtime: O(min(size1, size2))
Space: O(1)
*Runtime and space assumes that compare
runs in O(1) time and space.