Lavorare con gli iteratori
Nette\Utils\Iterables è una classe statica con funzioni per lavorare con gli iteratori. La sua controparte per gli array è Nette\Utils\Arrays.
Installazione:
composer require nette/utils
Tutti gli esempi presuppongono che sia creato questo alias:
use Nette\Utils\Iterables;
contains(iterable $iterable, $value): bool
Cerca un determinato valore all'interno di un iteratore. Usa il confronto rigoroso (===) per verificare la
corrispondenza. Restituisce true se il valore viene trovato, altrimenti false.
Iterables::contains(new ArrayIterator([1, 2, 3]), 1); // true
Iterables::contains(new ArrayIterator([1, 2, 3]), '1'); // false
Questo metodo è utile quando dovete stabilire rapidamente se un determinato valore esiste in un iteratore, senza scorrere manualmente tutti gli elementi.
containsKey(iterable $iterable, $key): bool
Cerca una determinata chiave all'interno di un iteratore. Usa il confronto rigoroso (===) per verificare la
corrispondenza. Restituisce true se la chiave viene trovata, altrimenti 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
Controlla se tutti gli elementi dell'iteratore soddisfano la condizione definita in $predicate. La callback
$predicate ha la firma function ($value, $key, iterable $iterable): bool e deve restituire
true per ogni elemento perché il metodo every() restituisca true.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isBelowThreshold = fn($value) => $value < 40;
$res = Iterables::every($iterator, $isBelowThreshold); // true
Questo metodo è utile per verificare che tutti gli elementi di una collezione soddisfino una certa condizione, per esempio se tutti i numeri sono minori di un determinato valore.
filter(iterable $iterable, callable $predicate): Generator
Crea un nuovo iteratore che contiene solo gli elementi dell'iteratore originale che soddisfano la condizione definita in
$predicate. La callback $predicate ha la firma
function ($value, $key, iterable $iterable): bool e deve restituire true per gli elementi da
conservare.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::filter($iterator, fn($v) => $v < 3);
// 1, 2
Il metodo usa un generatore, quindi il filtraggio avviene via via che scorrete il risultato. È efficiente in termini di memoria e permette di elaborare collezioni molto grandi. Se non scorrete tutti gli elementi dell'iteratore risultante, risparmiate potenza di calcolo, perché non tutti gli elementi dell'iteratore originale vengono elaborati.
first(iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed
Restituisce il primo elemento dell'iteratore. Se è indicato $predicate, restituisce il primo elemento che
soddisfa la condizione. La callback $predicate ha la firma
function ($value, $key, iterable $iterable): bool. Se non viene trovato alcun elemento corrispondente, viene chiamata
la callback $else (se indicata) e ne viene restituito il risultato. Se $else non è indicata, viene
restituito 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
Questo metodo è utile quando vi serve ottenere rapidamente il primo elemento di una collezione, o il primo elemento che soddisfa una determinata condizione, senza scorrere manualmente l'intera collezione.
firstKey(iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed
Restituisce la chiave del primo elemento dell'iteratore. Se è indicato $predicate, restituisce la chiave del
primo elemento che soddisfa la condizione. La callback $predicate ha la firma
function ($value, $key, iterable $iterable): bool. Se non viene trovato alcun elemento corrispondente, viene chiamata
la callback $else (se indicata) e ne viene restituito il risultato. Se $else non è indicata, viene
restituito 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 nuovo iteratore applicando la callback $transformer a ogni elemento dell'iteratore originale. La callback
$transformer ha la firma function ($value, $key, iterable $iterable): mixed e il suo valore di ritorno
viene usato come nuovo valore dell'elemento.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::map($iterator, fn($v) => $v * 2);
// 2, 4, 6
Il metodo usa un generatore, quindi la trasformazione avviene via via che scorrete il risultato. È efficiente in termini di memoria e permette di elaborare collezioni molto grandi. Se non scorrete tutti gli elementi dell'iteratore risultante, risparmiate potenza di calcolo, perché non tutti gli elementi dell'iteratore originale vengono elaborati.
mapWithKeys(iterable $iterable, callable $transformer): Generator
Crea un nuovo iteratore trasformando i valori e le chiavi dell'iteratore originale. La callback $transformer ha
la firma function ($value, $key, iterable $iterable): ?array{$newKey, $newValue}. Se $transformer
restituisce null, l'elemento viene saltato. Per gli elementi conservati, il primo elemento dell'array restituito
viene usato come nuova chiave e il secondo come nuovo valore.
$iterator = new ArrayIterator(['a' => 1, 'b' => 2]);
$iterator = Iterables::mapWithKeys($iterator, fn($v, $k) => $v > 1 ? [$v * 2, strtoupper($k)] : null);
// [4 => 'B']
Come map(), anche questo metodo usa un generatore per un'elaborazione progressiva e un uso parsimonioso della
memoria. Permette così di lavorare con collezioni grandi e di risparmiare potenza di calcolo quando si scorre solo in parte il
risultato.
memoize(iterable $iterable): IteratorAggregate
Crea un involucro attorno a un iteratore che, durante l'iterazione, mette in cache le sue chiavi e i suoi valori. Permette così di iterare più volte sui dati senza dover attraversare di nuovo la fonte originale.
$iterator = /* ... */; // dati che non si possono iterare più volte
$memoized = Iterables::memoize($iterator);
// ora potete iterare $memoized più volte senza perdere dati
Questo metodo è utile nelle situazioni in cui dovete iterare più volte sullo stesso insieme di dati, ma l'iteratore originale non permette iterazioni ripetute, oppure attraversarlo di nuovo sarebbe costoso (per esempio leggere i dati da un database o da un file).
repeatable(callable $factory): IteratorAggregate
Permette di iterare ripetutamente oggetti che altrimenti non lo consentono, di norma i generatori di PHP. Il metodo repeatable()
risolve elegantemente il problema: invece di passare l'iteratore stesso, passate una funzione che lo crea. Questa factory viene
poi chiamata automaticamente a ogni ciclo di iterazione.
// un normale generatore, che non si può iterare due volte
$generator = function () {
yield 'A';
yield 'B';
};
$iterator = Iterables::repeatable($generator);
foreach ($iterator as $v) echo $v; // stampa: AB
foreach ($iterator as $v) echo $v; // stampa: AB (il generatore è stato eseguito di nuovo)
Questo metodo è un'alternativa a memoize() nelle situazioni in cui lavorate con grandi quantità
di dati, perché repeatable() non mette i dati in cache, ma li genera di nuovo a ogni iterazione.
some(iterable $iterable, callable $predicate): bool
Controlla se almeno un elemento dell'iteratore soddisfa la condizione definita in $predicate. La callback
$predicate ha la firma function ($value, $key, iterable $iterable): bool e deve restituire
true per almeno un elemento perché il metodo some() restituisca true.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isEven = fn($value) => $value % 2 === 0;
$res = Iterables::some($iterator, $isEven); // true
Questo metodo è utile per verificare rapidamente se almeno un elemento di una collezione soddisfa una certa condizione, per esempio se la collezione contiene almeno un numero pari.
Vedi every().
toIterator(iterable $iterable): Iterator
Converte qualsiasi oggetto iterabile (array, Traversable) in un Iterator. Se l'input è già un Iterator, viene restituito invariato.
$array = [1, 2, 3];
$iterator = Iterables::toIterator($array);
// ora avete un Iterator al posto di un array
Questo metodo è utile quando dovete essere certi di avere a disposizione un Iterator, indipendentemente dal tipo dei dati in ingresso. Può tornare comodo quando create funzioni che lavorano con vari tipi di dati iterabili.