Trabajar con iteradores
Nette\Utils\Iterables es una clase estática con funciones para trabajar con iteradores. Su equivalente para los arrays es Nette\Utils\Arrays.
Instalación:
composer require nette/utils
Todos los ejemplos suponen que está creado el siguiente alias:
use Nette\Utils\Iterables;
contains (iterable $iterable, $value): bool
Busca un valor dado dentro de un iterador. Usa comparación estricta (===) para comprobar la coincidencia.
Devuelve true si encuentra el valor, y false en caso contrario.
Iterables::contains(new ArrayIterator([1, 2, 3]), 1); // true
Iterables::contains(new ArrayIterator([1, 2, 3]), '1'); // false
Este método resulta útil cuando necesita saber rápidamente si un valor concreto está en un iterador, sin recorrer manualmente todos los elementos.
containsKey (iterable $iterable, $key): bool
Busca una clave dada dentro de un iterador. Usa comparación estricta (===) para comprobar la coincidencia.
Devuelve true si encuentra la clave, y false en caso contrario.
Iterables::containsKey(new ArrayIterator([1, 2, 3]), 0); // true
Iterables::containsKey(new ArrayIterator([1, 2, 3]), 4); // false
every (iterable $iterable, callable $predicate): bool
Comprueba si todos los elementos del iterador cumplen la condición definida en $predicate. El callback
$predicate tiene la firma function ($value, $key, iterable $iterable): bool y debe devolver
true para todos los elementos para que el método every() devuelva true.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isBelowThreshold = fn($value) => $value < 40;
$res = Iterables::every($iterator, $isBelowThreshold); // true
Este método resulta útil para verificar que todos los elementos de una colección cumplen cierta condición, por ejemplo, si todos los números son menores que un valor dado.
filter (iterable $iterable, callable $predicate): Generator
Crea un iterador nuevo que contiene solo aquellos elementos del original que cumplen la condición definida en
$predicate. El callback $predicate tiene la firma
function ($value, $key, iterable $iterable): bool y debe devolver true para los elementos que deban
conservarse.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::filter($iterator, fn($v) => $v < 3);
// 1, 2
El método usa un generador, lo que significa que el filtrado se produce de forma incremental a medida que recorre el resultado. Esto es eficiente en memoria y permite procesar colecciones muy grandes. Si no recorre todos los elementos del iterador resultante, ahorra capacidad de cálculo, porque no se procesan todos los elementos del iterador original.
first (iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed
Devuelve el primer elemento del iterador. Si se indica $predicate, devuelve el primer elemento que cumple la
condición. El callback $predicate tiene la firma function ($value, $key, iterable $iterable): bool. Si
no se encuentra ningún elemento que encaje, se llama al callback $else (si se ha indicado) y se devuelve su
resultado. Si no se indica $else, se devuelve 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
Este método resulta útil cuando necesita obtener rápidamente el primer elemento de una colección, o el primero que cumple una condición concreta, sin recorrer manualmente toda la colección.
firstKey (iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed
Devuelve la clave del primer elemento del iterador. Si se indica $predicate, devuelve la clave del primer elemento
que cumple la condición. El callback $predicate tiene la firma
function ($value, $key, iterable $iterable): bool. Si no se encuentra ningún elemento que encaje, se llama al
callback $else (si se ha indicado) y se devuelve su resultado. Si no se indica $else, se devuelve
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
Crea un iterador nuevo aplicando el callback $transformer a cada elemento del iterador original. El callback
$transformer tiene la firma function ($value, $key, iterable $iterable): mixed y su valor de retorno se
usa como nuevo valor del elemento.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::map($iterator, fn($v) => $v * 2);
// 2, 4, 6
El método usa un generador, lo que significa que la transformación se produce de forma incremental a medida que recorre el resultado. Esto es eficiente en memoria y permite procesar colecciones muy grandes. Si no recorre todos los elementos del iterador resultante, ahorra capacidad de cálculo, porque no se procesan todos los elementos del iterador original.
mapWithKeys (iterable $iterable, callable $transformer): Generator
Crea un iterador nuevo transformando los valores y las claves del original. El callback $transformer tiene la
firma function ($value, $key, iterable $iterable): ?array{$newKey, $newValue}. Si $transformer devuelve
null, el elemento se salta. En los elementos conservados, el primer elemento del array devuelto se usa como nueva
clave y el segundo, como nuevo valor.
$iterator = new ArrayIterator(['a' => 1, 'b' => 2]);
$iterator = Iterables::mapWithKeys($iterator, fn($v, $k) => $v > 1 ? [$v * 2, strtoupper($k)] : null);
// [4 => 'B']
Igual que map(), este método usa un generador para el procesamiento incremental y la eficiencia en memoria. Esto
permite trabajar con colecciones grandes y ahorrar capacidad de cálculo cuando solo se recorre parcialmente el resultado.
memoize (iterable $iterable): IteratorAggregate
Crea un envoltorio alrededor de un iterador que guarda en caché sus claves y valores durante la iteración. Esto permite iterar repetidamente sobre los datos sin tener que recorrer de nuevo el origen original.
$iterator = /* ... */; // datos que no se pueden recorrer varias veces
$memoized = Iterables::memoize($iterator);
// Ahora puede recorrer $memoized varias veces sin perder datos
Este método resulta útil cuando necesita iterar varias veces sobre el mismo conjunto de datos, pero el iterador original no permite repetir la iteración o volver a recorrerlo sería costoso (por ejemplo, leer datos de una base de datos o de un archivo).
repeatable (callable $factory): IteratorAggregate
Permite iterar repetidamente sobre objetos que de otro modo no lo admiten, normalmente los generadores de PHP. El método repeatable()
resuelve el problema con elegancia: en lugar de pasar el propio iterador, se pasa una función que lo crea. Esa factory se llama
después automáticamente en cada bucle de iteración.
// Un generador estándar que no se puede recorrer dos veces
$generator = function () {
yield 'A';
yield 'B';
};
$iterator = Iterables::repeatable($generator);
foreach ($iterator as $v) echo $v; // Imprime: AB
foreach ($iterator as $v) echo $v; // Imprime: AB (el generador se ejecutó de nuevo)
Este método es una alternativa a memoize() en situaciones donde trabaja con grandes cantidades
de datos, ya que repeatable() no cachea los datos, sino que los genera de nuevo en cada iteración.
some (iterable $iterable, callable $predicate): bool
Comprueba si al menos un elemento del iterador cumple la condición definida en $predicate. El callback
$predicate tiene la firma function ($value, $key, iterable $iterable): bool y debe devolver
true para al menos un elemento para que el método some() devuelva true.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isEven = fn($value) => $value % 2 === 0;
$res = Iterables::some($iterator, $isEven); // true
Este método resulta útil para verificar rápidamente si al menos un elemento de una colección cumple cierta condición, por ejemplo, si la colección contiene al menos un número par.
Vea every().
toIterator (iterable $iterable): Iterator
Convierte cualquier objeto iterable (array, Traversable) en un Iterator. Si la entrada ya es un Iterator, se devuelve sin cambios.
$array = [1, 2, 3];
$iterator = Iterables::toIterator($array);
// Ahora tiene un Iterator en lugar de un array
Este método resulta útil cuando necesita asegurarse de disponer de un Iterator, sea cual sea el tipo de datos de entrada. Puede venir bien al crear funciones que trabajan con distintos tipos de datos iterables.