core/Iter
Utilities for Iter
(iterator) values.
Iterators are a way to represent sequences of values that can be lazily produced. They can be used to:
- Iterate over collections.
- Represent collections that are too large to fit in memory or that are produced incrementally.
- Transform collections without creating intermediate collections.
Iterators are inherently stateful. Calling next
"consumes" a value from
the Iterator that cannot be put back, so keep that in mind when sharing
iterators between consumers.
import Iter "mo:core/Iter";
An iterator can be iterated over using a for
loop:
let iter = [1, 2, 3].values();
for (x in iter) {
// do something with x...
}
Iterators can be:
- created from other collections (e.g. using
values
orkeys
function on aMap
) or from scratch (e.g. usingempty
orsingleton
). - transformed using
map
,filter
,concat
, etc. Which can be used to compose several transformations together without materializing intermediate collections. - consumed using
forEach
,size
,toArray
, etc. - combined using
concat
.
Type Iter
type Iter<T> = Types.Iter<T>
An iterator that produces values of type T
. Calling next
returns
null
when iteration is finished.
Iterators are inherently stateful. Calling next
"consumes" a value from
the Iterator that cannot be put back, so keep that in mind when sharing
iterators between consumers.
An iterator i
can be iterated over using
let iter = [1, 2, 3].values();
for (x in iter) {
// do something with x...
}
Function empty
func empty<T>() : Iter<T>
Creates an empty iterator.
for (x in Iter.empty<Nat>())
assert false; // This loop body will never run
Function singleton
func singleton<T>(value : T) : Iter<T>
Creates an iterator that produces a single value.
var sum = 0;
for (x in Iter.singleton(3))
sum += x;
assert sum == 3;
Function forEach
func forEach<T>(iter : Iter<T>, f : (T) -> ())
Calls a function f
on every value produced by an iterator and discards
the results. If you're looking to keep these results use map
instead.
var sum = 0;
Iter.forEach<Nat>([1, 2, 3].values(), func(x) {
sum += x;
});
assert sum == 6;
Function enumerate
func enumerate<T>(iter : Iter<T>) : Iter<(Nat, T)>
Takes an iterator and returns a new iterator that pairs each element with its index. The index starts at 0 and increments by 1 for each element.
let iter = Iter.fromArray(["A", "B", "C"]);
let enumerated = Iter.enumerate(iter);
let result = Iter.toArray(enumerated);
assert result == [(0, "A"), (1, "B"), (2, "C")];
Function step
func step<T>(iter : Iter<T>, n : Nat) : Iter<T>
Creates a new iterator that yields every nth element from the original iterator.
If interval
is 0, returns an empty iterator. If interval
is 1, returns the original iterator.
For any other positive interval, returns an iterator that skips interval - 1
elements after each yielded element.
let iter = Iter.fromArray([1, 2, 3, 4, 5, 6]);
let steppedIter = Iter.step(iter, 2); // Take every 2nd element
assert ?1 == steppedIter.next();
assert ?3 == steppedIter.next();
assert ?5 == steppedIter.next();
assert null == steppedIter.next();
Function size
func size<T>(iter : Iter<T>) : Nat
Consumes an iterator and counts how many elements were produced (discarding them in the process).
let iter = [1, 2, 3].values();
assert 3 == Iter.size(iter);
Function map
func map<T, R>(iter : Iter<T>, f : T -> R) : Iter<R>
Takes a function and an iterator and returns a new iterator that lazily applies the function to every element produced by the argument iterator.
let iter = [1, 2, 3].values();
let mappedIter = Iter.map<Nat, Nat>(iter, func (x) = x * 2);
let result = Iter.toArray(mappedIter);
assert result == [2, 4, 6];
Function filter
func filter<T>(iter : Iter<T>, f : T -> Bool) : Iter<T>
Creates a new iterator that only includes elements from the original iterator for which the predicate function returns true.
let iter = [1, 2, 3, 4, 5].values();
let evenNumbers = Iter.filter<Nat>(iter, func (x) = x % 2 == 0);
let result = Iter.toArray(evenNumbers);
assert result == [2, 4];
Function filterMap
func filterMap<T, R>(iter : Iter<T>, f : T -> ?R) : Iter<R>
Creates a new iterator by applying a transformation function to each element of the original iterator. Elements for which the function returns null are excluded from the result.
let iter = [1, 2, 3].values();
let evenNumbers = Iter.filterMap<Nat, Nat>(iter, func (x) = if (x % 2 == 0) ?x else null);
let result = Iter.toArray(evenNumbers);
assert result == [2];
Function flatten
func flatten<T>(iter : Iter<Iter<T>>) : Iter<T>
Flattens an iterator of iterators into a single iterator by concatenating the inner iterators.
Possible optimization: Use flatMap
when you need to transform elements before calling flatten
. Example: use flatMap(...)
instead of flatten(map(...))
.
let iter = Iter.flatten([[1, 2].values(), [3].values(), [4, 5, 6].values()].values());
let result = Iter.toArray(iter);
assert result == [1, 2, 3, 4, 5, 6];
Function flatMap
func flatMap<T, R>(iter : Iter<T>, f : T -> Iter<R>) : Iter<R>
Transforms every element of an iterator into an iterator and concatenates the results.
let iter = Iter.flatMap<Nat, Nat>([1, 3, 5].values(), func (x) = [x, x + 1].values());
let result = Iter.toArray(iter);
assert result == [1, 2, 3, 4, 5, 6];
Function take
func take<T>(iter : Iter<T>, n : Nat) : Iter<T>
Returns a new iterator that yields at most, first n
elements from the original iterator.
After n
elements have been produced or the original iterator is exhausted,
subsequent calls to next()
will return null
.
let iter = Iter.fromArray([1, 2, 3, 4, 5]);
let first3 = Iter.take(iter, 3);
let result = Iter.toArray(first3);
assert result == [1, 2, 3];
let iter = Iter.fromArray([1, 2, 3]);
let first5 = Iter.take(iter, 5);
let result = Iter.toArray(first5);
assert result == [1, 2, 3]; // only 3 elements in the original iterator
Function takeWhile
func takeWhile<T>(iter : Iter<T>, f : T -> Bool) : Iter<T>
Returns a new iterator that yields elements from the original iterator until the predicate function returns false. The first element for which the predicate returns false is not included in the result.
let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]);
let result = Iter.takeWhile<Nat>(iter, func (x) = x < 4);
let array = Iter.toArray(result);
assert array == [1, 2, 3]; // note the difference between `takeWhile` and `filter`
Function drop
func drop<T>(iter : Iter<T>, n : Nat) : Iter<T>
Returns a new iterator that skips the first n
elements from the original iterator.
If the original iterator has fewer than n
elements, the result will be an empty iterator.
let iter = Iter.fromArray([1, 2, 3, 4, 5]);
let skipped = Iter.drop(iter, 3);
let result = Iter.toArray(skipped);
assert result == [4, 5];
Function dropWhile
func dropWhile<T>(iter : Iter<T>, f : T -> Bool) : Iter<T>
Returns a new iterator that skips elements from the original iterator until the predicate function returns false. The first element for which the predicate returns false is the first element produced by the new iterator.
let iter = Iter.fromArray([1, 2, 3, 4, 5, 4, 3, 2, 1]);
let result = Iter.dropWhile<Nat>(iter, func (x) = x < 4);
let array = Iter.toArray(result);
assert array == [4, 5, 4, 3, 2, 1]; // notice that `takeWhile` and `dropWhile` are complementary
Function zip
func zip<A, B>(a : Iter<A>, b : Iter<B>) : Iter<(A, B)>
Zips two iterators into a single iterator that produces pairs of elements. The resulting iterator will stop producing elements when either of the input iterators is exhausted.
let iter1 = [1, 2, 3].values();
let iter2 = ["A", "B"].values();
let zipped = Iter.zip(iter1, iter2);
let result = Iter.toArray(zipped);
assert result == [(1, "A"), (2, "B")]; // note that the third element from iter1 is not included, because iter2 is exhausted
Function zip3
func zip3<A, B, C>(a : Iter<A>, b : Iter<B>, c : Iter<C>) : Iter<(A, B, C)>
Zips three iterators into a single iterator that produces triples of elements. The resulting iterator will stop producing elements when any of the input iterators is exhausted.
let iter1 = ["A", "B"].values();
let iter2 = ["1", "2", "3"].values();
let iter3 = ["x", "y", "z", "xd"].values();
let zipped = Iter.zip3(iter1, iter2, iter3);
let result = Iter.toArray(zipped);
assert result == [("A", "1", "x"), ("B", "2", "y")]; // note that the unmatched elements from iter2 and iter3 are not included
Function zipWith
func zipWith<A, B, R>(a : Iter<A>, b : Iter<B>, f : (A, B) -> R) : Iter<R>
Zips two iterators into a single iterator by applying a function to zipped pairs of elements. The resulting iterator will stop producing elements when either of the input iterators is exhausted.
let iter1 = ["A", "B"].values();
let iter2 = ["1", "2", "3"].values();
let zipped = Iter.zipWith<Text, Text, Text>(iter1, iter2, func (a, b) = a # b);
let result = Iter.toArray(zipped);
assert result == ["A1", "B2"]; // note that the third element from iter2 is not included, because iter1 is exhausted
Function zipWith3
func zipWith3<A, B, C, R>(a : Iter<A>, b : Iter<B>, c : Iter<C>, f : (A, B, C) -> R) : Iter<R>
Zips three iterators into a single iterator by applying a function to zipped triples of elements. The resulting iterator will stop producing elements when any of the input iterators is exhausted.
let iter1 = ["A", "B"].values();
let iter2 = ["1", "2", "3"].values();
let iter3 = ["x", "y", "z", "xd"].values();
let zipped = Iter.zipWith3<Text, Text, Text, Text>(iter1, iter2, iter3, func (a, b, c) = a # b # c);
let result = Iter.toArray(zipped);
assert result == ["A1x", "B2y"]; // note that the unmatched elements from iter2 and iter3 are not included
Function all
func all<T>(iter : Iter<T>, f : T -> Bool) : Bool
Checks if a predicate function is true for all elements produced by an iterator. It stops consuming elements from the original iterator as soon as the predicate returns false.
assert Iter.all<Nat>([1, 2, 3].values(), func (x) = x < 4);
assert not Iter.all<Nat>([1, 2, 3].values(), func (x) = x < 3);
Function any
func any<T>(iter : Iter<T>, f : T -> Bool) : Bool
Checks if a predicate function is true for any element produced by an iterator. It stops consuming elements from the original iterator as soon as the predicate returns true.
assert Iter.any<Nat>([1, 2, 3].values(), func (x) = x == 2);
assert not Iter.any<Nat>([1, 2, 3].values(), func (x) = x == 4);
Function find
func find<T>(iter : Iter<T>, f : T -> Bool) : ?T
Finds the first element produced by an iterator for which a predicate function returns true.
Returns null
if no such element is found.
It stops consuming elements from the original iterator as soon as the predicate returns true.
let iter = [1, 2, 3, 4].values();
assert ?2 == Iter.find<Nat>(iter, func (x) = x % 2 == 0);
Function findIndex
func findIndex<T>(iter : Iter<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 iter = ['A', 'B', 'C', 'D'].values();
let found = Iter.findIndex<Char>(iter, 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 contains
func contains<T>(iter : Iter<T>, equal : (T, T) -> Bool, value : T) : Bool
Checks if an element is produced by an iterator. It stops consuming elements from the original iterator as soon as the predicate returns true.
import Nat "mo:core/Nat";
let iter = [1, 2, 3, 4].values();
assert Iter.contains<Nat>(iter, Nat.equal, 2);
Function foldLeft
func foldLeft<T, R>(iter : Iter<T>, initial : R, combine : (R, T) -> R) : R
Reduces an iterator to a single value by applying a function to each element and an accumulator.
The accumulator is initialized with the initial
value.
It starts applying the combine
function starting from the initial
accumulator value and the first elements produced by the iterator.
let iter = ["A", "B", "C"].values();
let result = Iter.foldLeft<Text, Text>(iter, "S", func (acc, x) = "(" # acc # x # ")");
assert result == "(((SA)B)C)";
Function foldRight
func foldRight<T, R>(iter : Iter<T>, initial : R, combine : (T, R) -> R) : R
Reduces an iterator to a single value by applying a function to each element in reverse order and an accumulator.
The accumulator is initialized with the initial
value and it is first combined with the last element produced by the iterator.
It starts applying the combine
function starting from the last elements produced by the iterator.
Performance note: Since this function needs to consume the entire iterator to reverse it,
it has to materialize the entire iterator in memory to get to the last element to start applying the combine
function.
Use foldLeft
or reduce
when possible to avoid the extra memory overhead.
let iter = ["A", "B", "C"].values();
let result = Iter.foldRight<Text, Text>(iter, "S", func (x, acc) = "(" # x # acc # ")");
assert result == "(A(B(CS)))";
Function reduce
func reduce<T>(iter : Iter<T>, combine : (T, T) -> T) : ?T
Reduces an iterator to a single value by applying a function to each element, starting with the first elements.
The accumulator is initialized with the first element produced by the iterator.
When the iterator is empty, it returns null
.
import Nat "mo:core/Nat";
let iter = [1, 2, 3].values();
assert ?6 == Iter.reduce<Nat>(iter, Nat.add);
Function scanLeft
func scanLeft<T, R>(iter : Iter<T>, initial : R, combine : (R, T) -> R) : Iter<R>
Produces an iterator containing cumulative results of applying the combine
operator going left to right, including the initial
value.
import Nat "mo:core/Nat";
let iter = [1, 2, 3].values();
let scanned = Iter.scanLeft<Nat, Nat>(iter, 0, Nat.add);
let result = Iter.toArray(scanned);
assert result == [0, 1, 3, 6];
Function scanRight
func scanRight<T, R>(iter : Iter<T>, initial : R, combine : (T, R) -> R) : Iter<R>
Produces an iterator containing cumulative results of applying the combine
operator going right to left, including the initial
value.
Performance note: Since this function needs to consume the entire iterator to reverse it,
it has to materialize the entire iterator in memory to get to the last element to start applying the combine
function.
Use scanLeft
when possible to avoid the extra memory overhead.
import Nat "mo:core/Nat";
let iter = [1, 2, 3].values();
let scanned = Iter.scanRight<Nat, Nat>(iter, 0, Nat.add);
let result = Iter.toArray(scanned);
assert result == [0, 3, 5, 6];
Function unfold
func unfold<T, S>(initial : S, step : S -> ?(T, S)) : Iter<T>
Creates an iterator that produces elements using the step
function starting from the initial
value.
The step
function takes the current state and returns the next element and the next state, or null
if the iteration is finished.
let iter = Iter.unfold<Nat, Nat>(1, func (x) = if (x <= 3) ?(x, x + 1) else null);
let result = Iter.toArray(iter);
assert result == [1, 2, 3];
Function max
func max<T>(iter : Iter<T>, compare : (T, T) -> Order.Order) : ?T
Consumes an iterator and returns the first maximum element produced by the iterator.
If the iterator is empty, it returns null
.
import Nat "mo:core/Nat";
let iter = [1, 2, 3].values();
assert ?3 == Iter.max<Nat>(iter, Nat.compare);
Function min
func min<T>(iter : Iter<T>, compare : (T, T) -> Order.Order) : ?T
Consumes an iterator and returns the first minimum element produced by the iterator.
If the iterator is empty, it returns null
.
import Nat "mo:core/Nat";
let iter = [1, 2, 3].values();
assert ?1 == Iter.min<Nat>(iter, Nat.compare);
Function infinite
func infinite<T>(item : T) : Iter<T>
Creates an iterator that produces an infinite sequence of x
.
let iter = Iter.infinite(10);
assert ?10 == iter.next();
assert ?10 == iter.next();
assert ?10 == iter.next();
// ...
Function concat
func concat<T>(a : Iter<T>, b : Iter<T>) : Iter<T>
Takes two iterators and returns a new iterator that produces elements from the original iterators sequentally.
let iter1 = [1, 2].values();
let iter2 = [5, 6, 7].values();
let concatenatedIter = Iter.concat(iter1, iter2);
let result = Iter.toArray(concatenatedIter);
assert result == [1, 2, 5, 6, 7];
Function fromArray
func fromArray<T>(array : [T]) : Iter<T>
Creates an iterator that produces the elements of an Array in ascending index order.
let iter = Iter.fromArray([1, 2, 3]);
assert ?1 == iter.next();
assert ?2 == iter.next();
assert ?3 == iter.next();
assert null == iter.next();
Function fromVarArray
func fromVarArray<T>(array : [var T]) : Iter<T>
Like fromArray
but for Arrays with mutable elements. Captures
the elements of the Array at the time the iterator is created, so
further modifications won't be reflected in the iterator.
Function toArray
func toArray<T>(iter : Iter<T>) : [T]
Consumes an iterator and collects its produced elements in an Array.
let iter = [1, 2, 3].values();
assert [1, 2, 3] == Iter.toArray(iter);
Function toVarArray
func toVarArray<T>(iter : Iter<T>) : [var T]
Like toArray
but for Arrays with mutable elements.
Function sort
func sort<T>(iter : Iter<T>, compare : (T, T) -> Order.Order) : Iter<T>
Sorted iterator. Will iterate over all elements to sort them, necessarily.
Function repeat
func repeat<T>(item : T, count : Nat) : Iter<T>
Creates an iterator that produces a given item a specified number of times.
let iter = Iter.repeat<Nat>(3, 2);
assert ?3 == iter.next();
assert ?3 == iter.next();
assert null == iter.next();
Runtime: O(1)
Space: O(1)
Function reverse
func reverse<T>(iter : Iter<T>) : Iter<T>
Creates a new iterator that produces elements from the original iterator in reverse order. Note: This function needs to consume the entire iterator to reverse it.
let iter = Iter.fromArray([1, 2, 3]);
let reversed = Iter.reverse(iter);
assert ?3 == reversed.next();
assert ?2 == reversed.next();
assert ?1 == reversed.next();
assert null == reversed.next();
Runtime: O(n) where n is the number of elements in the iterator
Space: O(n) where n is the number of elements in the iterator