イテレータの扱い

Nette\Utils\Iterables は、イテレータを扱う関数を集めた静的クラスです。配列に対する対応物は Nette\Utils\Arraysです。

インストール:

composer require nette/utils

以下の例では、次の別名が作られているものとします。

use Nette\Utils\Iterables;

contains (iterable $iterable, $value)bool

イテレータの中で指定した値を探します。一致の判定には厳密な比較(===)を使います。値が見つかれば true、そうでなければ false を返します。

Iterables::contains(new ArrayIterator([1, 2, 3]), 1);    // true
Iterables::contains(new ArrayIterator([1, 2, 3]), '1');  // false

このメソッドは、全要素を手作業で辿らずに、特定の値がイテレータにあるかを素早く判定したいときに便利です。

containsKey (iterable $iterable, $key)bool

イテレータの中で指定したキーを探します。一致の判定には厳密な比較(===)を使います。キーが見つかれば true、そうでなければ false を返します。

Iterables::containsKey(new ArrayIterator([1, 2, 3]), 0);  // true
Iterables::containsKey(new ArrayIterator([1, 2, 3]), 4);  // false

every (iterable $iterable, callable $predicate)bool

イテレータのすべての要素が $predicate で定義された条件を満たすかを調べます。コールバック $predicate のシグネチャは function ($value, $key, iterable $iterable): bool で、every() メソッドが true を返すには、すべての要素に対して true を返さなければなりません。

$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isBelowThreshold = fn($value) => $value < 40;
$res = Iterables::every($iterator, $isBelowThreshold); // true

このメソッドは、コレクションのすべての要素がある条件を満たすか、たとえばすべての数がある値より小さいかを確かめるのに便利です。

filter (iterable $iterable, callable $predicate): Generator

もとのイテレータのうち、$predicate で定義された条件を満たす要素だけを含む新しいイテレータを作ります。コールバック $predicate のシグネチャは function ($value, $key, iterable $iterable): bool で、残すべき要素に対して true を返さなければなりません。

$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::filter($iterator, fn($v) => $v < 3);
// 1, 2

このメソッドはジェネレータを使うので、結果を辿るのにつれて絞り込みが少しずつ行われます。メモリ効率がよく、とても大きなコレクションも処理できます。結果のイテレータをすべて辿らなければ、もとのイテレータの全要素が処理されないので計算資源も節約できます。

first (iterable $iterable, ?callable $predicate=null, ?callable $else=null)mixed

イテレータの最初の要素を返します。$predicate を渡すと、条件を満たす最初の要素を返します。コールバック $predicate のシグネチャは function ($value, $key, iterable $iterable): bool です。一致する要素がなければ、(渡されていれば)$else コールバックが呼ばれ、その結果が返ります。$else が渡されていなければ null が返ります。

Iterables::first(new ArrayIterator([1, 2, 3]));                   // 1
Iterables::first(new ArrayIterator([1, 2, 3]), fn($v) => $v > 2); // 3
Iterables::first(new ArrayIterator([]));                          // null
Iterables::first(new ArrayIterator([]), else: fn() => false);     // false

このメソッドは、コレクション全体を手作業で辿らずに、最初の要素や特定の条件を満たす最初の要素を素早く得たいときに便利です。

firstKey (iterable $iterable, ?callable $predicate=null, ?callable $else=null)mixed

イテレータの最初の要素のキーを返します。$predicate を渡すと、条件を満たす最初の要素のキーを返します。コールバック $predicate のシグネチャは function ($value, $key, iterable $iterable): bool です。一致する要素がなければ、(渡されていれば)$else コールバックが呼ばれ、その結果が返ります。$else が渡されていなければ null が返ります。

Iterables::firstKey(new ArrayIterator([1, 2, 3]));                   // 0
Iterables::firstKey(new ArrayIterator([1, 2, 3]), fn($v) => $v > 2); // 2
Iterables::firstKey(new ArrayIterator(['a' => 1, 'b' => 2]));        // 'a'
Iterables::firstKey(new ArrayIterator([]));                          // null

map (iterable $iterable, callable $transformer): Generator

もとのイテレータの各要素に $transformer コールバックを適用して、新しいイテレータを作ります。コールバック $transformer のシグネチャは function ($value, $key, iterable $iterable): mixed で、その戻り値が要素の新しい値として使われます。

$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::map($iterator, fn($v) => $v * 2);
// 2, 4, 6

このメソッドはジェネレータを使うので、結果を辿るのにつれて変換が少しずつ行われます。メモリ効率がよく、とても大きなコレクションも処理できます。結果のイテレータをすべて辿らなければ、もとのイテレータの全要素が処理されないので計算資源も節約できます。

mapWithKeys (iterable $iterable, callable $transformer): Generator

もとのイテレータの値とキーを変換して、新しいイテレータを作ります。コールバック $transformer のシグネチャは function ($value, $key, iterable $iterable): ?array{$newKey, $newValue} です。$transformernull を返すと、その要素は飛ばされます。残す要素については、返された配列の最初の要素が新しいキー、2 つめが新しい値として使われます。

$iterator = new ArrayIterator(['a' => 1, 'b' => 2]);
$iterator = Iterables::mapWithKeys($iterator, fn($v, $k) => $v > 1 ? [$v * 2, strtoupper($k)] : null);
// [4 => 'B']

map() と同じく、このメソッドもジェネレータを使って少しずつ処理し、メモリ効率を保ちます。おかげで大きなコレクションを扱え、結果を途中までしか辿らない場合には計算資源を節約できます。

memoize (iterable $iterable): IteratorAggregate

イテレータを包み、反復中にそのキーと値をキャッシュするラッパーを作ります。おかげで、もとのデータ源をもう一度辿らずに、同じデータを繰り返し反復できます。

$iterator = /* ... */; // 複数回は反復できないデータ
$memoized = Iterables::memoize($iterator);
// これで $memoized をデータを失わずに何度でも反復できます

このメソッドは、同じデータの集まりを何度も反復する必要があるのに、もとのイテレータが繰り返しの反復を許さない場合や、辿り直すのが高くつく場合(データベースやファイルからの読み込みなど)に便利です。

repeatable (callable $factory): IteratorAggregate

ふつうは繰り返しの反復に対応していないオブジェクト、典型的には PHP のジェネレータを、繰り返し反復できるようにします。repeatable() メソッドはこの問題をきれいに解きます。イテレータそのものを渡す代わりに、それを作る関数を渡すのです。このファクトリが反復のたびに自動的に呼ばれます。

// 2 回反復できない通常のジェネレータ
$generator = function () {
	yield 'A';
	yield 'B';
};

$iterator = Iterables::repeatable($generator);

foreach ($iterator as $v) echo $v; // 出力: AB
foreach ($iterator as $v) echo $v; // 出力: AB(ジェネレータがもう一度走った)

このメソッドは、大量のデータを扱う場面で memoize()の代わりになります。repeatable() はデータをキャッシュせず、反復のたびに生成し直すからです。

some (iterable $iterable, callable $predicate)bool

イテレータの少なくともひとつの要素が $predicate で定義された条件を満たすかを調べます。コールバック $predicate のシグネチャは function ($value, $key, iterable $iterable): bool で、some() メソッドが true を返すには、少なくともひとつの要素に対して true を返さなければなりません。

$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isEven = fn($value) => $value % 2 === 0;
$res = Iterables::some($iterator, $isEven); // true

このメソッドは、コレクションの少なくともひとつの要素がある条件を満たすか、たとえば偶数がひとつでも含まれているかを素早く確かめるのに便利です。

every()をご覧ください。

toIterator (iterable $iterable): Iterator

任意の反復可能なオブジェクト(配列、Traversable)を Iterator に変換します。入力がすでに Iterator なら、そのまま返します。

$array = [1, 2, 3];
$iterator = Iterables::toIterator($array);
// これで配列の代わりに Iterator が手に入りました

このメソッドは、入力のデータ型に関係なく Iterator が使える状態を保証したいときに便利です。さまざまな種類の反復可能なデータを扱う関数を作るときに役立ちます。

バージョン: 4.x