module Iterator(T)

Overview

AnIterator allows processing sequences lazily, as opposed toEnumerablewhich processes sequences eagerly and produces anArray in most of its methods.

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

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

The aboveworks, but creates many intermediate arrays: one for the#select call, one for the#map call and one for the#first call. A more efficientway is to invoke Range#eachwithout a block,which gives us anIterator sowe can process the operations lazily:

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

Iterator redefines many ofEnumerable's method in a lazyway, returning iterators instead of arrays.

At the end of the call chainwe get back a new iterator:we need to consume it, either using#each orEnumerable#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? andsize – subsequent callswill give a different result as therewill be less elements to consume.

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

Iterating step-by-step

An iterator returns its next element on the method#next. Its return type is a union of the iterator's element type and theStop type: T | Iterator::Stop. The stop type is a sentinel valuewhich indicates that the iterator has reached its end. It usually needs to be handled and filtered out in order to use the element value for anything useful. UnlikeNil it's not an implicitly falsey type.

iter = (1..5).each

# Unfiltered elements contain `Iterator::Stop` type
iter.next + iter.next # Error: expected argument #1 to 'Int32#+' to be Int32, not (Int32 | Iterator::Stop)

# Type filtering eliminates the stop type
a = iter.next
b = iter.next
unless a.is_a?(Iterator::Stop) || b.is_a?(Iterator::Stop)
  a + b # => 3
end

Iterator::Stop is only present in the return type of#next. All other methods remove it from their return types.

Iterators can be used to build a loop.

iter = (1..5).each
sum = 0
while !(elem = iter.next).is_a?(Iterator::Stop)
  sum += elem
end
sum # => 15

Implementing an Iterator

To implement anIterator you need to define a#next method that must return the next element in the sequence orIterator::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 ofN 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, likeArray,Hash,Range,String andIO. Usually to get an iterator you invoke a method thatwould usually yield elements to a block, butwithout passing a block:Array#each,Array#each_index,Hash#each,String#each_char, IO#each_line, etc.

Included Modules

Direct including types

Defined in:

iterator.cr
json/from_json.cr
json/to_json.cr
yaml/to_yaml.cr

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module Enumerable(T)

accumulate(initial : U) : Array(U) forall U
accumulate : Array(T)
accumulate(initial : U, &block : U, T -> U) : Array(U) forall U
accumulate(&block : T, T -> T) : Array(T)
accumulate
, all?(& : T -> ) : Bool
all?(pattern) : Bool
all? : Bool
all?
, any?(& : T -> ) : Bool
any?(pattern) : Bool
any? : Bool
any?
, chunks(&block : T -> U) forall U chunks, compact_map(& : T -> _) compact_map, count(& : T -> ) : Int32
count(item) : Int32
count
, cycle(n, & : T -> ) : Nil
cycle(& : T -> ) : Nil
cycle
, each(& : T -> ) each, each_cons(count : Int, reuse = false, &) each_cons, each_cons_pair(& : T, T -> ) : Nil each_cons_pair, each_slice(count : Int, reuse = false, &) each_slice, each_step(n : Int, *, offset : Int = 0, & : T -> ) : Nil each_step, each_with_index(offset = 0, &) each_with_index, each_with_object(obj : U, & : T, U -> ) : U forall U each_with_object, empty? : Bool empty?, find(if_none = nil, & : T -> ) find, find!(& : T -> ) : T find!, find_value(if_none = nil, & : T -> ) find_value, first(&)
first(count : Int) : Array(T)
first : T
first
, first? : T | Nil first?, flat_map(& : T -> _) flat_map, group_by(& : T -> U) forall U group_by, in_groups_of(size : Int, filled_up_with : U = nil) forall U
in_groups_of(size : Int, filled_up_with : U = nil, reuse = false, &) forall U
in_groups_of
, in_slices_of(size : Int) : Array(Array(T)) in_slices_of, includes?(obj) : Bool includes?, index(& : T -> ) : Int32 | Nil
index(obj) : Int32 | Nil
index
, index!(& : T -> ) : Int32
index!(obj) : Int32
index!
, index_by(& : T -> U) : Hash(U, T) forall U index_by, join(io : IO, separator = "") : Nil
join(separator, io : IO) : Nil
join(separator = "") : String
join(io : IO, separator = "", & : T, IO -> )
join(separator, io : IO, &)
join(separator = "", & : T -> )
join
, map(& : T -> U) : Array(U) forall U map, map_with_index(offset = 0, & : T, Int32 -> U) : Array(U) forall U map_with_index, max(count : Int) : Array(T)
max : T
max
, max? : T | Nil max?, max_by(& : T -> U) : T forall U max_by, max_by?(& : T -> U) : T | Nil forall U max_by?, max_of(& : T -> U) : U forall U max_of, max_of?(& : T -> U) : U | Nil forall U max_of?, min(count : Int) : Array(T)
min : T
min
, min? : T | Nil min?, min_by(& : T -> U) : T forall U min_by, min_by?(& : T -> U) : T | Nil forall U min_by?, min_of(& : T -> U) : U forall U min_of, min_of?(& : T -> U) : U | Nil forall U min_of?, minmax : Tuple(T, T) minmax, minmax? : Tuple(T | Nil, T | Nil) minmax?, minmax_by(& : T -> U) : Tuple(T, T) forall U minmax_by, minmax_by?(& : T -> U) : Tuple(T, T) | Tuple(Nil, Nil) forall U minmax_by?, minmax_of(& : T -> U) : Tuple(U, U) forall U minmax_of, minmax_of?(& : T -> U) : Tuple(U, U) | Tuple(Nil, Nil) forall U minmax_of?, none?(& : T -> ) : Bool
none?(pattern) : Bool
none? : Bool
none?
, one?(& : T -> ) : Bool
one?(pattern) : Bool
one? : Bool
one?
, partition(& : T -> ) : Tuple(Array(T), Array(T))
partition(type : U.class) forall U
partition
, present? : Bool present?, product(initial : Number)
product
product(initial : Number, & : T -> )
product(& : T -> _)
product
, reduce(memo, &)
reduce(&)
reduce
, reduce?(&) reduce?, reject(& : T -> )
reject(type : U.class) forall U
reject(pattern) : Array(T)
reject
, sample(n : Int, random : Random | Nil = nil) : Array(T)
sample(random : Random | Nil = nil) : T
sample
, select(& : T -> )
select(type : U.class) : Array(U) forall U
select(pattern) : Array(T)
select
, size : Int32 size, skip(count : Int) skip, skip_while(& : T -> ) : Array(T) skip_while, sum(initial)
sum
sum(initial, & : T -> )
sum(& : T -> )
sum
, take_while(& : T -> ) : Array(T) take_while, tally(hash)
tally : Hash(T, Int32)
tally
, tally_by(hash, &)
tally_by(&block : T -> U) : Hash(U, Int32) forall U
tally_by
, to_a : Array(T)
to_a(& : T -> U) : Array(U) forall U
to_a
, to_h
to_h(& : T -> Tuple(K, V)) forall K, V
to_h
, to_set : Set(T)
to_set(&block : T -> U) : Set(U) forall U
to_set
, zip(*others : Indexable | Iterable | Iterator, &)
zip(*others : Indexable | Iterable | Iterator)
zip
, zip?(*others : Indexable | Iterable | Iterator, &)
zip?(*others : Indexable | Iterable | Iterator)
zip?

Class methods inherited from module Enumerable(T)

element_type(x) element_type

Constructor Detail

def self.new(pull : JSON::PullParser) #

Creates a new iteratorwhich iterates over a JSON array. See alsoIterator#from_json.

WARNING TheJSON::PullParser can't be used by anything else until the iterator is fully consumed.


Class Method Detail

def self.chain(iters : Iterator(Iter)) forall Iter #

The same as#chain, but have better performancewhen 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

def self.chain(iters : Iterable(Iter)) forall Iter #

the same as.chain(Iterator(Iter))


def self.empty #

Returns an empty iterator.


def self.from_json(string_or_io) #

Reads the content of a JSON array into an iterator in a lazyway. With this method it should be possible to process a huge JSON array,without the requirement that thewhole array fits into memory.

The following example produces a huge file, uses a lot of CPU but should not require much memory.

File.open("/tmp/test.json", "w+") do |f|
  (0..1_000_000_000).each.to_json(f)
end

File.open("/tmp/test.json", "r") do |f|
  p Iterator(Int32).from_json(f).skip(1_000_000_000).to_a
end

WARNING Thestring_or_io can't be used by anything else until the iterator is fully consumed.


def self.of(element : T) #

def self.of(&block : -> T) #

def self.stop #

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


Instance Method Detail

def accumulate(initial : U) forall U #

Returns an iterator that returnsinitial and its prefix sumswith the original iterator's elements.

ExpectsU to respond to the#+ method.

iter = (3..6).each.accumulate(7)
iter.next # => 7
iter.next # => 10
iter.next # => 14
iter.next # => 19
iter.next # => 25
iter.next # => Iterator::Stop::INSTANCE

def accumulate #

Returns an iterator that returns the prefix sums of the original iterator's elements.

ExpectsT to respond to the#+ method.

iter = (3..6).each.accumulate
iter.next # => 3
iter.next # => 7
iter.next # => 12
iter.next # => 18
iter.next # => Iterator::Stop::INSTANCE

def accumulate(initial : U, &block : U, T -> U) forall U #

Returns an iterator that accumulatesinitialwith the original iterator's elements by the givenblock.

Similar to#accumulate(&block : T, T -> T), except the initial value is provided by an argument and needs not have the same type as the elements of the original iterator. This initial value is returned first.

iter = [4, 3, 2].each.accumulate("X") { |x, y| x * y }
iter.next # => "X"
iter.next # => "XXXX"
iter.next # => "XXXXXXXXXXXX"
iter.next # => "XXXXXXXXXXXXXXXXXXXXXXXX"
iter.next # => Iterator::Stop::INSTANCE

def accumulate(&block : T, T -> T) #

Returns an iterator that accumulates the original iterator's elements by the givenblock.

For each element of the original iterator the block is passed an accumulator value and the element. The result becomes the new value for the accumulator and is then returned. The initial value for the accumulator is the first element of the original iterator.

iter = %w(the quick brown fox).each.accumulate { |x, y| "#{x}, #{y}" }
iter.next # => "the"
iter.next # => "the, quick"
iter.next # => "the, quick, brown"
iter.next # => "the, quick, brown, fox"
iter.next # => Iterator::Stop::INSTANCE

def 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 performancewhen 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

def 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 elementswhich 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 chunkwhen invoking#next.

  • Ifreuse is given, the array can be reused
  • Ifreuse is anArray, this arraywill be reused
  • Ifreuse is truthy, the methodwill create a new array and reuse it.

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

See also:Enumerable#chunks.


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

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

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 slicewhen invoking#next.

  • Ifreuse isfalse, the methodwill create a new array for each chunk
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is anArray, that arraywill be reused

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

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


def compact_map(&func : T -> _) #

Returns an iterator that applies the given function to the element and then returns it unless it isnil. If the returned valuewould benil it instead returns the next nonnil value.

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

def cons(n : Int, reuse = false) #

Returns an iterator that returns consecutive chunks of the sizen.

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.

  • Ifreuse is given, the array can be reused
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is an instance ofArray,Deque or a similar collection type (implementing#<<,#shift and#size) itwill be used.
  • Ifreuse is falsey, the arraywill not be reused.

This can be used to prevent many memory allocationswhen 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 ofn == 2which avoids heap allocations.


def 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 n == 2 to avoid heap allocations.


def cycle(n : Int) #

Returns an iterator that repeatedly returns the elements of the original iterator starting back at the beginningwhen the endwas reached, but onlyn 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

def cycle #

Returns an iterator that repeatedly returns the elements of the original iterator forever starting back at the beginningwhen the endwas 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

def each #

def each(& : T -> ) : 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"

def each_slice(n, reuse = false) #

Returns an iterator that then returns slices ofn 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 consecutivewhen invoking#next.

  • Ifreuse is given, the array can be reused
  • Ifreuse is anArray, this arraywill be reused
  • Ifreuse is truthy, the methodwill create a new array and reuse it.

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


def first(n : Int) #

Returns an iterator that only returns the firstn elements of the initial iterator.

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

def flat_map(&func : T -> _) #

Returns a new iteratorwith the concatenated results of running the block once for every element in the collection. OnlyArray andIterator 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]

def 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

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

Returns an iterator that chunks the iterator's elements in arrays ofsize filling up the remaining elements if no element remainswithnil 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.

  • Ifreuse is given, the array can be reused
  • Ifreuse is anArray, this arraywill be reused
  • Ifreuse is truthy, the methodwill create a new array and reuse it.

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


def 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

abstract def next #

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


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

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

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

def reject(type : U.class) forall U #

Returns an iterator that only returns elements that arenot of the giventype.

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

def reject(pattern) #

Returns an iterator that only returns elements wherepattern === 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

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

Returns an iterator that only returns elements forwhich 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

def select(type : U.class) forall U #

Returns an iterator that only returns elements of the giventype.

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

def select(pattern) #

Returns an iterator that only returns elements wherepattern === 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

def skip(n : Int) #

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

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

def 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

def slice(n : Int, reuse = false) #

Alias of#each_slice.


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

Returns an iterator over chunks of elements,where each chunk ends rightafter the given block's value istruthy.

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 slicewhen invoking#next.

  • Ifreuse isfalse, the methodwill create a new array for each chunk
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is anArray, that arraywill be reused

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


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

Returns an iterator over chunks of elements,where each chunk ends rightafter the given pattern is matched withpattern === 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 slicewhen invoking#next.

  • Ifreuse isfalse, the methodwill create a new array for each chunk
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is anArray, that arraywill be reused

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


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

Returns an iterator over chunks of elements,where each chunk ends rightbefore the given block's value istruthy.

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 slicewhen invoking#next.

  • Ifreuse isfalse, the methodwill create a new array for each chunk
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is anArray, that arraywill be reused

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


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

Returns an iterator over chunks of elements,where each chunk ends rightbefore the given pattern is matched withpattern === 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 slicewhen invoking#next.

  • Ifreuse isfalse, the methodwill create a new array for each chunk
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is anArray, that arraywill be reused

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


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

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

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 slicewhen invoking#next.

  • Ifreuse isfalse, the methodwill create a new array for each chunk
  • Ifreuse istrue, the methodwill create a new array and reuse it.
  • Ifreuse is anArray, that arraywill be reused

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

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


def step(n : Int) #

Returns an iterator that only returns everynth element, startingwith the first.

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

def stop #

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


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

Returns an iterator that returns elementswhile 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

def tap(&block : T -> ) #

Returns an iterator that calls the given blockwith the next element of the iteratorwhen 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

def to_json(json : JSON::Builder) #

Converts the content of an iterator into a JSON array in lazyway. SeeIterator#from_json for an example.


def to_yaml(yaml : YAML::Nodes::Builder) #

Converts the content of an iterator to YAML. The conversion is done in a lazyway. In contrast toIterator#to_json this operation requires memory for the for the complete YAML document


def 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

def 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

def with_index(offset : Int = 0) #

Returns an iterator that returns aTuple 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

def with_index(offset : Int = 0, &) #

Yields each element in this iterator togetherwith its index.


def with_object(obj) #

Returns an iterator that returns aTuple 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

def with_object(obj, &) #

Yields each element in this iterator togetherwithobj. Returns that object.


def zip(*others : Iterator) : Iterator #

Returns an iterator that returns the elements of this iterator andothers traversed in tandem asTuples.

Iteration stopswhen 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