Skip to content

module Iterator(T)

An Iterator allows processing sequences lazily, as opposed to Enumerable which processes sequences eagerly and produces an Array in most of its methods.

As an example, let's compute the first three numbers in the range 1..10_000_000 that are even, multiplied by three. One way to do this is:

(1..10_000_000).select(&.even?).map { |x| x * 3 }.first(3) # => [6, 12, 18]

The above works, but creates many intermediate arrays: one for the select call, one for the map call and one for the take call. A more efficient way is to invoke Range#each without a block, which gives us an Iterator so we can process the operations lazily:

(1..10_000_000).each.select(&.even?).map { |x| x * 3 }.first(3) # => #< Iterator(T)::First...

Iterator redefines many of Enumerable's method in a lazy way, returning iterators instead of arrays.

At the end of the call chain we get back a new iterator: we need to consume it, either using each or Enumerable#to_a:

(1..10_000_000).each.select(&.even?).map { |x| x * 3 }.first(3).to_a # => [6, 12, 18]

Because iterators only go forward, when using methods that consume it entirely or partially – to_a, any?, count, none?, one? and size – subsequent calls will give a different result as there will be less elements to consume.

iter = (0...100).each
iter.size # => 100
iter.size # => 0

To implement an Iterator you need to define a next method that must return the next element in the sequence or Iterator::Stop::INSTANCE, which signals the end of the sequence (you can invoke stop inside an iterator as a shortcut).

For example, this is an iterator that returns a sequence of N zeros:

class Zeros
  include Iterator(Int32)

  def initialize(@size : Int32)
    @produced = 0
  end

  def next
    if @produced < @size
      @produced += 1
      0
    else
      stop
    end
  end
end

zeros = Zeros.new(5)
zeros.to_a # => [0, 0, 0, 0, 0]

The standard library provides iterators for many classes, like Array, Hash, Range, String and IO. Usually to get an iterator you invoke a method that would usually yield elements to a block, but without passing a block: Array#each, Array#each_index, Hash#each, String#each_char, IO#each_line, etc.

Included modules

Enumerable

Direct including types

Steppable::StepIterator(T, L, B)

Class methods

.chain(iters : Iterator(Iter)) forall Iter

The same as #chain, but have better performance when the quantity of iterators to chain is large (usually greater than 4) or undetermined.

array_of_iters = [[1], [2, 3], [4, 5, 6]].each.map &.each
iter = Iterator(Int32).chain array_of_iters
iter.next # => 1
iter.next # => 2
iter.next # => 3
iter.next # => 4
View source

.chain(iters : Iterable(Iter)) forall Iter

View source

.of(element : T)

View source

.of(&block : -> T)

View source

.stop

Shortcut for Iterator::Stop::INSTANCE, to signal that there are no more elements in an iterator.

View source

Methods

#chain(other : Iterator(U)) forall U

Returns an iterator that returns elements from the original iterator until it is exhausted and then returns the elements of the second iterator. Compared to .chain(Iterator(Iter)), it has better performance when the quantity of iterators to chain is small (usually less than 4). This method also cannot chain iterators in a loop, for that see .chain(Iterator(Iter)).

iter = (1..2).each.chain(('a'..'b').each)
iter.next # => 1
iter.next # => 2
iter.next # => 'a'
iter.next # => 'b'
iter.next # => Iterator::Stop::INSTANCE
View source

#chunk(reuse = false, &block : T -> U) forall T, U

Returns an Iterator that enumerates over the items, chunking them together based on the return value of the block.

Consecutive elements which return the same block value are chunked together.

For example, consecutive even numbers and odd numbers can be chunked as follows.

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk(&.even?).each do |even, ary|
  p [even, ary]
end

# => [false, [3, 1]]
#    [true, [4]]
#    [false, [1, 5, 9]]
#    [true, [2, 6]]
#    [false, [5, 3, 5]]

The following key values have special meaning:

By default, a new array is created and yielded for each chunk when invoking next. * If reuse is given, the array can be reused * If reuse is an Array, this array will be reused * If reuse is truthy, the method will create a new array and reuse it.

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

See also: Enumerable#chunks.

View source

#chunk_while(reuse : Bool | Array(T) = false, &block : T, T -> B) forall B

Returns an iterator for each chunked elements where elements are kept in a given chunk as long as the block's value over a pair of elements is truthy.

For example, one-by-one increasing subsequences can be chunked as follows:

ary = [1, 2, 4, 9, 10, 11, 12, 15, 16, 19, 20, 21]
iter = ary.chunk_while { |i, j| i + 1 == j }
iter.next # => [1, 2]
iter.next # => [4]
iter.next # => [9, 10, 11, 12]
iter.next # => [15, 16]
iter.next # => [19, 20, 21]
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each slice when invoking next. * If reuse is false, the method will create a new array for each chunk * If reuse is true, the method will create a new array and reuse it. * If reuse is an Array, that array will be reused

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

See also #slice_when, which works similarly but the block's condition is inverted.

View source

#compact_map(&func : T -> _)

Returns an iterator that applies the given function to the element and then returns it unless it is nil. If the returned value would be nil it instead returns the next non nil value.

iter = [1, nil, 2, nil].each.compact_map { |e| e.try &.*(2) }
iter.next # => 2
iter.next # => 4
iter.next # => Iterator::Stop::INSTANCE
View source

#cons(n : Int, reuse = false)

Returns an iterator that returns consecutive chunks of the size n.

iter = (1..5).each.cons(3)
iter.next # => [1, 2, 3]
iter.next # => [2, 3, 4]
iter.next # => [3, 4, 5]
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and returned for each consecutive call of next. * If reuse is given, the array can be reused * If reuse is true, the method will create a new array and reuse it. * If reuse is an instance of Array, Deque or a similar collection type (implementing #<<, #shift and #size) it will be used. * If reuse is falsey, the array will not be reused.

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

Chunks of two items can be iterated using #cons_pair, an optimized implementation for the special case of size == 2 which avoids heap allocations.

View source

#cons_pair : Iterator(Tuple(T, T))

Returns an iterator that returns consecutive pairs of adjacent items.

iter = (1..5).each.cons_pair
iter.next # => {1, 2}
iter.next # => {2, 3}
iter.next # => {3, 4}
iter.next # => {4, 5}
iter.next # => Iterator::Stop::INSTANCE

Chunks of more than two items can be iterated using #cons. This method is just an optimized implementation for the special case of size == 2 to avoid heap allocations.

View source

#cycle(n : Int)

Returns an iterator that repeatedly returns the elements of the original iterator starting back at the beginning when the end was reached, but only n times.

iter = ["a", "b", "c"].each.cycle(2)
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => Iterator::Stop::INSTANCE
View source

#cycle

Returns an iterator that repeatedly returns the elements of the original iterator forever starting back at the beginning when the end was reached.

iter = ["a", "b", "c"].each.cycle
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => "a"
iter.next # => "b"
iter.next # => "c"
iter.next # => "a"
# and so an and so on
View source

#each

View source

#each(&) : Nil

Calls the given block once for each element, passing that element as a parameter.

iter = ["a", "b", "c"].each
iter.each { |x| print x, " " } # Prints "a b c"
View source

#each_slice(n, reuse = false)

Returns an iterator that then returns slices of n elements of the initial iterator.

iter = (1..9).each.each_slice(3)
iter.next # => [1, 2, 3]
iter.next # => [4, 5, 6]
iter.next # => [7, 8, 9]
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each consecutive when invoking next. * If reuse is given, the array can be reused * If reuse is an Array, this array will be reused * If reuse is truthy, the method will create a new array and reuse it.

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

View source

#first(n : Int)

Returns an iterator that only returns the first n elements of the initial iterator.

iter = ["a", "b", "c"].each.first 2
iter.next # => "a"
iter.next # => "b"
iter.next # => Iterator::Stop::INSTANCE
View source

#flat_map(&func : T -> _)

Returns a new iterator with the concatenated results of running the block once for every element in the collection. Only Array and Iterator results are concatenated; every other value is returned once in the new iterator.

iter = [1, 2, 3].each.flat_map { |x| [x, x] }

iter.next # => 1
iter.next # => 1
iter.next # => 2

iter = [1, 2, 3].each.flat_map { |x| [x, x].each }

iter.to_a # => [1, 1, 2, 2, 3, 3]
View source

#flatten

Returns an iterator that flattens nested iterators and arrays into a single iterator whose type is the union of the simple types of all of the nested iterators and arrays (and their nested iterators and arrays, and so on).

iter = [(1..2).each, ('a'..'b').each].each.flatten
iter.next # => 1
iter.next # => 2
iter.next # => 'a'
iter.next # => 'b'
iter.next # => Iterator::Stop::INSTANCE
View source

#in_groups_of(size : Int, filled_up_with = nil, reuse = false)

Returns an iterator that chunks the iterator's elements in arrays of size filling up the remaining elements if no element remains with nil or a given optional parameter.

iter = (1..3).each.in_groups_of(2)
iter.next # => [1, 2]
iter.next # => [3, nil]
iter.next # => Iterator::Stop::INSTANCE
iter = (1..3).each.in_groups_of(2, 'z')
iter.next # => [1, 2]
iter.next # => [3, 'z']
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each group. * If reuse is given, the array can be reused * If reuse is an Array, this array will be reused * If reuse is truthy, the method will create a new array and reuse it.

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

View source

#map(&func : T -> U) forall U

Returns an iterator that applies the given block to the next element and returns the result.

iter = [1, 2, 3].each.map &.*(2)
iter.next # => 2
iter.next # => 4
iter.next # => 6
iter.next # => Iterator::Stop::INSTANCE
View source

abstract #next

Returns the next element in this iterator, or Iterator::Stop::INSTANCE if there are no more elements.

View source

#reject(&func : T -> U) forall U

Returns an iterator that only returns elements for which the passed in block returns a falsey value.

iter = [1, 2, 3].each.reject &.odd?
iter.next # => 2
iter.next # => Iterator::Stop::INSTANCE
View source

#reject(type : U.class) forall U

Returns an iterator that only returns elements that are not of the given type.

iter = [1, false, 3, true].each.reject(Bool)
iter.next # => 1
iter.next # => 3
iter.next # => Iterator::Stop::INSTANCE
View source

#reject(pattern)

Returns an iterator that only returns elements where pattern === element does not hold.

iter = [2, 3, 1, 5, 4, 6].each.reject(3..5)
iter.next # => 2
iter.next # => 1
iter.next # => 6
iter.next # => Iterator::Stop::INSTANCE
View source

#select(&func : T -> U) forall U

Returns an iterator that only returns elements for which the passed in block returns a truthy value.

iter = [1, 2, 3].each.select &.odd?
iter.next # => 1
iter.next # => 3
iter.next # => Iterator::Stop::INSTANCE
View source

#select(type : U.class) forall U

Returns an iterator that only returns elements of the given type.

iter = [1, false, 3, nil].each.select(Int32)
iter.next # => 1
iter.next # => 3
iter.next # => Iterator::Stop::INSTANCE
View source

#select(pattern)

Returns an iterator that only returns elements where pattern === element.

iter = [1, 3, 2, 5, 4, 6].each.select(3..5)
iter.next # => 3
iter.next # => 5
iter.next # => 4
iter.next # => Iterator::Stop::INSTANCE
View source

#skip(n : Int)

Returns an iterator that skips the first n elements and only returns the elements after that.

iter = (1..3).each.skip(2)
iter.next # -> 3
iter.next # -> Iterator::Stop::INSTANCE
View source

#skip_while(&func : T -> U) forall U

Returns an iterator that only starts to return elements once the given block has returned falsey value for one element.

iter = [1, 2, 3, 4, 0].each.skip_while { |i| i < 3 }
iter.next # => 3
iter.next # => 4
iter.next # => 0
iter.next # => Iterator::Stop::INSTANCE
View source

#slice(n : Int, reuse = false)

Alias of each_slice.

View source

#slice_after(pattern, reuse : Bool | Array(T) = false)

Returns an iterator over chunks of elements, where each chunk ends right after the given pattern is matched with pattern === element.

For example, to get chunks that end at each ASCII uppercase letter:

ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']
#                   ^         ^    ^
iter = ary.slice_after('A'..'Z')
iter.next # => ['a', 'b', 'C']
iter.next # => ['d', 'E']
iter.next # => ['F']
iter.next # => ['g', 'h']
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each slice when invoking next. * If reuse is false, the method will create a new array for each chunk * If reuse is true, the method will create a new array and reuse it. * If reuse is an Array, that array will be reused

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

View source

#slice_after(reuse : Bool | Array(T) = false, &block : T -> B) forall B

Returns an iterator over chunks of elements, where each chunk ends right after the given block's value is truthy.

For example, to get chunks that end at each uppercase letter:

ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']
#                   ^         ^    ^
iter = ary.slice_after(&.uppercase?)
iter.next # => ['a', 'b', 'C']
iter.next # => ['d', 'E']
iter.next # => ['F']
iter.next # => ['g', 'h']
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each slice when invoking next. * If reuse is false, the method will create a new array for each chunk * If reuse is true, the method will create a new array and reuse it. * If reuse is an Array, that array will be reused

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

View source

#slice_before(pattern, reuse : Bool | Array(T) = false)

Returns an iterator over chunks of elements, where each chunk ends right before the given pattern is matched with pattern === element.

For example, to get chunks that end just before each ASCII uppercase letter:

ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']
#              ^         ^    ^
iter = ary.slice_before('A'..'Z')
iter.next # => ['a', 'b']
iter.next # => ['C', 'd']
iter.next # => ['E']
iter.next # => ['F', 'g', 'h']
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each slice when invoking next. * If reuse is false, the method will create a new array for each chunk * If reuse is true, the method will create a new array and reuse it. * If reuse is an Array, that array will be reused

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

View source

#slice_before(reuse : Bool | Array(T) = false, &block : T -> B) forall B

Returns an iterator over chunks of elements, where each chunk ends right before the given block's value is truthy.

For example, to get chunks that end just before each uppercase letter:

ary = ['a', 'b', 'C', 'd', 'E', 'F', 'g', 'h']
#              ^         ^    ^
iter = ary.slice_before(&.uppercase?)
iter.next # => ['a', 'b']
iter.next # => ['C', 'd']
iter.next # => ['E']
iter.next # => ['F', 'g', 'h']
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each slice when invoking next. * If reuse is false, the method will create a new array for each chunk * If reuse is true, the method will create a new array and reuse it. * If reuse is an Array, that array will be reused

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

View source

#slice_when(reuse : Bool | Array(T) = false, &block : T, T -> B) forall B

Returns an iterator for each chunked elements where the ends of chunks are defined by the block, when the block's value over a pair of elements is truthy.

For example, one-by-one increasing subsequences can be chunked as follows:

ary = [1, 2, 4, 9, 10, 11, 12, 15, 16, 19, 20, 21]
iter = ary.slice_when { |i, j| i + 1 != j }
iter.next # => [1, 2]
iter.next # => [4]
iter.next # => [9, 10, 11, 12]
iter.next # => [15, 16]
iter.next # => [19, 20, 21]
iter.next # => Iterator::Stop::INSTANCE

By default, a new array is created and yielded for each slice when invoking next. * If reuse is false, the method will create a new array for each chunk * If reuse is true, the method will create a new array and reuse it. * If reuse is an Array, that array will be reused

This can be used to prevent many memory allocations when each slice of interest is to be used in a read-only fashion.

See also #chunk_while, which works similarly but the block's condition is inverted.

View source

#step(n : Int)

Returns an iterator that only returns every nth element, starting with the first.

iter = (1..6).each.step(2)
iter.next # => 1
iter.next # => 3
iter.next # => 5
iter.next # => Iterator::Stop::INSTANCE
View source

#stop

Shortcut for Iterator::Stop::INSTANCE, to signal that there are no more elements in an iterator.

View source

#take_while(&func : T -> U) forall U

Returns an iterator that returns elements while the given block returns a truthy value.

iter = (1..5).each.take_while { |i| i < 3 }
iter.next # => 1
iter.next # => 2
iter.next # => Iterator::Stop::INSTANCE
View source

#tap(&block : T -> )

Returns an iterator that calls the given block with the next element of the iterator when calling next, still returning the original element.

a = 0
iter = (1..3).each.tap { |x| a += x }
iter.next # => 1
a         # => 1
iter.next # => 2
a         # => 3
iter.next # => 3
a         # => 6
iter.next # => Iterator::Stop::INSTANCE
View source

#uniq(&func : T -> U) forall U

Returns an iterator that only returns unique values of the original iterator. The provided block is applied to the elements to determine the value to be checked for uniqueness.

iter = [["a", "a"], ["b", "a"], ["a", "c"]].each.uniq &.first
iter.next # => ["a", "a"]
iter.next # => ["b", "a"]
iter.next # => Iterator::Stop::INSTANCE
View source

#uniq

Returns an iterator that only returns unique values of the original iterator.

iter = [1, 2, 1].each.uniq
iter.next # => 1
iter.next # => 2
iter.next # => Iterator::Stop::INSTANCE
View source

#with_index(offset : Int = 0

Yields each element in this iterator together with its index.

View source

#with_index(offset : Int = 0)

Returns an iterator that returns a Tuple of the element and its index.

iter = (1..3).each.with_index
iter.next # => {1, 0}
iter.next # => {2, 1}
iter.next # => {3, 2}
iter.next # => Iterator::Stop::INSTANCE
View source

#with_object

Yields each element in this iterator together with obj. Returns that object.

View source

#with_object(obj)

Returns an iterator that returns a Tuple of the element and a given object.

iter = (1..3).each.with_object("a")
iter.next # => {1, "a"}
iter.next # => {2, "a"}
iter.next # => {3, "a"}
iter.next # => Iterator::Stop::INSTANCE
View source

#zip(*others : Iterator) : Iterator

Returns an iterator that returns the elements of this iterator and others traversed in tandem as Tuples.

Iteration stops when any of the iterators runs out of elements.

iter1 = [4, 5, 6].each
iter2 = [7, 8, 9].each
iter3 = ['a', 'b', 'c', 'd'].each
iter = iter1.zip(iter2, iter3)
iter.next # => {4, 7, 'a'}
iter.next # => {5, 8, 'b'}
iter.next # => {6, 9, 'c'}
iter.next # => Iterator::Stop::INSTANCE
View source