イテレータの扱い
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} です。$transformer が
null
を返すと、その要素は飛ばされます。残す要素については、返された配列の最初の要素が新しいキー、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 が使える状態を保証したいときに便利です。さまざまな種類の反復可能なデータを扱う関数を作るときに役立ちます。