lloyAlloy on GitHub
Iter
Docs

Iter

Updated
Iter<T>
import { Iter } from "@alloy/std/iter"

local players = game:GetService("Players"):GetPlayers()
local names = Iter.from(players)
    :filter(function(p) return p.Team ~= nil end)
    :map(function(p) return p.Name end)
    :take(5)
    :collect()
for i in Iter.range(1, 10, 2) do end

A lazy iterator, from the std: each step wraps the last, and nothing runs until collect, for_each, a reducer, or a for loop pulls. Iter.from takes an array, a function that returns the next value or nil, or a Set, Queue, Heap, or HashMap; it keeps the element type, so Iter.from(number[]) is Iter<number> and :collect() is number[]. Another table with an __iter metamethod needs a cast. Iter.range(from, to, step?) counts, inclusive.

map keeps the element type through any number of steps, so every callback in a chain knows its parameter type.

Statics

Iter.from

Iter.from<T>(source: T[] | (() -> T?) | Set<T> | Queue<T> | Heap<T> | HashMap<any, T>): Iter<T>

A lazy iterator over an array, a function that returns the next value or nil, or a std collection. It keeps the element type, so Iter.from(number[]) collects back to number[].

import { Iter } from "@alloy/std/iter"

local xs = Iter.from([ 1, 2, 3 ]):collect()
print(xs:len())

Iter.range

Iter.range(from: number, to: number, step: number?): Iter<number>

The numbers from from to to, both inclusive, by step. step defaults to 1.

import { Iter } from "@alloy/std/iter"

for i in Iter.range(1, 10, 2) do
    print(i)
end

Methods

Iter:next

Iter:next(): T?

Pulls one value, or nil at the end. Every other reader is built on it.

import { Iter } from "@alloy/std/iter"

local it = Iter.from([ 1, 2 ])
print(it:next())

Iter:map

Iter:map<U>(f: (T) -> U): Iter<U>

Each value as f(value), still lazily. The result keeps the new element type, however long the chain.

import { Iter } from "@alloy/std/iter"

local names = Iter.from([ 1, 2 ]):map(function(n) return `#{n}` end)
print(names:collect():join(", "))

Iter:filter

Iter:filter(f: (T) -> boolean): Iter<T>

The values where f(value) holds. Nothing runs until something pulls.

import { Iter } from "@alloy/std/iter"

local big = Iter.from([ 1, 5 ]):filter(function(n) return n > 2 end)
print(big:count())

Iter:take

Iter:take(n: number): Iter<T>

The first n values, then the end. The source is never pulled past them.

import { Iter } from "@alloy/std/iter"

print(Iter.range(1, 100):take(3):count())

Iter:skip

Iter:skip(n: number): Iter<T>

Everything after the first n values.

import { Iter } from "@alloy/std/iter"

print(Iter.range(1, 5):skip(3):count())

Iter:take_while

Iter:take_while(f: (T) -> boolean): Iter<T>

Values until f(value) first fails; that value and the rest are dropped.

import { Iter } from "@alloy/std/iter"

local it = Iter.range(1, 10):take_while(function(n) return n < 4 end)
print(it:count())

Iter:chain

Iter:chain(other: Iter<T>): Iter<T>

This iterator, then other, as one run.

import { Iter } from "@alloy/std/iter"

local both = Iter.from([ 1 ]):chain(Iter.from([ 2 ]))
print(both:count())

Iter:collect

Iter:collect(): T[]

Pulls every value into an array. This is what ends a chain.

import { Iter } from "@alloy/std/iter"

local xs = Iter.range(1, 3):collect()
print(xs:join(", "))

Iter:to_table

Iter:to_table(): { T }

The rest of the run, drained into a new table. An Iter holds no table of its own, so this one is a copy, and the pull spends the iterator: a second call gives an empty table.

import { Iter } from "@alloy/std/iter"

local t = Iter.range(1, 3):to_table()
print(#t)

Iter:for_each

Iter:for_each(f: (T) -> ())

Pulls every value and calls f on it.

import { Iter } from "@alloy/std/iter"

Iter.from([ 1, 2 ]):for_each(function(n) print(n) end)

Iter:count

Iter:count(): number

Pulls every value and returns how many there were. It consumes the iterator.

import { Iter } from "@alloy/std/iter"

print(Iter.range(1, 5):count())

Iter:any

Iter:any(f: (T) -> boolean): boolean

Whether f holds for some value. It stops at the first hit.

import { Iter } from "@alloy/std/iter"

print(Iter.from([ 1, 5 ]):any(function(n) return n > 3 end))

Iter:all

Iter:all(f: (T) -> boolean): boolean

Whether f holds for every value. It stops at the first miss.

import { Iter } from "@alloy/std/iter"

print(Iter.from([ 1, 5 ]):all(function(n) return n > 0 end))

Iter:find

Iter:find(f: (T) -> boolean): T?

The first value where f holds, or nil. It stops there, so the rest is never pulled.

import { Iter } from "@alloy/std/iter"

print(Iter.range(1, 10):find(function(n) return n % 4 == 0 end))

Iter:reduce

Iter:reduce<U>(f: (U, T) -> U, init: U): U

Folds the run: acc = f(acc, value) from init, and the last acc is the result.

import { Iter } from "@alloy/std/iter"

local sum = Iter.range(1, 4):reduce(function(acc: number, n) return acc + n end, 0)
print(sum)

Iter:first

Iter:first(): T?

The first value, or nil for an empty run. It pulls once.

import { Iter } from "@alloy/std/iter"

print(Iter.from([ 1, 2 ]):first())

Iter:last

Iter:last(): T?

The last value, or nil for an empty run. It pulls the whole run.

import { Iter } from "@alloy/std/iter"

print(Iter.from([ 1, 2 ]):last())