Praca z iteratorami
Nette\Utils\Iterables to klasa statyczna z funkcjami do pracy z iteratorami. Jej odpowiednikiem dla tablic jest Nette\Utils\Arrays.
Instalacja:
composer require nette/utils
Wszystkie przykłady zakładają, że utworzony jest następujący alias:
use Nette\Utils\Iterables;
contains(iterable $iterable, $value): bool
Szuka podanej wartości w iteratorze. Do sprawdzenia zgodności używa porównania ścisłego (===). Zwraca
true, jeśli wartość zostanie znaleziona, w przeciwnym razie false.
Iterables::contains(new ArrayIterator([1, 2, 3]), 1); // true
Iterables::contains(new ArrayIterator([1, 2, 3]), '1'); // false
Ta metoda przydaje się, gdy potrzebujesz szybko ustalić, czy w iteratorze istnieje konkretna wartość, bez ręcznego przechodzenia przez wszystkie elementy.
containsKey(iterable $iterable, $key): bool
Szuka podanego klucza w iteratorze. Do sprawdzenia zgodności używa porównania ścisłego (===). Zwraca
true, jeśli klucz zostanie znaleziony, w przeciwnym razie 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
Sprawdza, czy wszystkie elementy iteratora spełniają warunek zdefiniowany w $predicate. Callback
$predicate ma sygnaturę function ($value, $key, iterable $iterable): bool i musi zwrócić
true dla każdego elementu, aby metoda every() zwróciła true.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isBelowThreshold = fn($value) => $value < 40;
$res = Iterables::every($iterator, $isBelowThreshold); // true
Ta metoda przydaje się do sprawdzenia, czy wszystkie elementy kolekcji spełniają określony warunek, na przykład czy wszystkie liczby są mniejsze od konkretnej wartości.
filter(iterable $iterable, callable $predicate): Generator
Tworzy nowy iterator zawierający tylko te elementy pierwotnego iteratora, które spełniają warunek zdefiniowany w
$predicate. Callback $predicate ma sygnaturę
function ($value, $key, iterable $iterable): bool i musi zwrócić true dla elementów, które mają
zostać zachowane.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::filter($iterator, fn($v) => $v < 3);
// 1, 2
Metoda używa generatora, co oznacza, że filtrowanie zachodzi stopniowo, w miarę przechodzenia po wyniku. Jest to oszczędne pamięciowo i pozwala przetwarzać bardzo duże kolekcje. Jeśli nie przejdziesz po wszystkich elementach wynikowego iteratora, oszczędzasz moc obliczeniową, bo nie wszystkie elementy pierwotnego iteratora zostaną przetworzone.
first(iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed
Zwraca pierwszy element iteratora. Jeśli podano $predicate, zwraca pierwszy element spełniający warunek.
Callback $predicate ma sygnaturę function ($value, $key, iterable $iterable): bool. Jeśli żaden
pasujący element się nie znajdzie, wywoływany jest callback $else (o ile go podano) i zwracany jest jego wynik.
Jeśli $else nie podano, zwracane jest 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
Ta metoda przydaje się, gdy potrzebujesz szybko pobrać pierwszy element kolekcji albo pierwszy element spełniający konkretny warunek, bez ręcznego przechodzenia przez całą kolekcję.
firstKey(iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed
Zwraca klucz pierwszego elementu iteratora. Jeśli podano $predicate, zwraca klucz pierwszego elementu
spełniającego warunek. Callback $predicate ma sygnaturę
function ($value, $key, iterable $iterable): bool. Jeśli żaden pasujący element się nie znajdzie, wywoływany
jest callback $else (o ile go podano) i zwracany jest jego wynik. Jeśli $else nie podano, zwracane
jest 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
Tworzy nowy iterator, stosując callback $transformer do każdego elementu pierwotnego iteratora. Callback
$transformer ma sygnaturę function ($value, $key, iterable $iterable): mixed, a zwracana przez niego
wartość jest używana jako nowa wartość elementu.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::map($iterator, fn($v) => $v * 2);
// 2, 4, 6
Metoda używa generatora, co oznacza, że przekształcanie zachodzi stopniowo, w miarę przechodzenia po wyniku. Jest to oszczędne pamięciowo i pozwala przetwarzać bardzo duże kolekcje. Jeśli nie przejdziesz po wszystkich elementach wynikowego iteratora, oszczędzasz moc obliczeniową, bo nie wszystkie elementy pierwotnego iteratora zostaną przetworzone.
mapWithKeys(iterable $iterable, callable $transformer): Generator
Tworzy nowy iterator, przekształcając wartości i klucze pierwotnego iteratora. Callback $transformer ma
sygnaturę function ($value, $key, iterable $iterable): ?array{$newKey, $newValue}. Jeśli $transformer
zwróci null, element zostanie pominięty. Dla zachowanych elementów pierwszy element zwróconej tablicy jest
używany jako nowy klucz, a drugi jako nowa wartość.
$iterator = new ArrayIterator(['a' => 1, 'b' => 2]);
$iterator = Iterables::mapWithKeys($iterator, fn($v, $k) => $v > 1 ? [$v * 2, strtoupper($k)] : null);
// [4 => 'B']
Podobnie jak map(), ta metoda używa generatora do przetwarzania stopniowego i oszczędności pamięci. Pozwala
to pracować z dużymi kolekcjami i oszczędzać moc obliczeniową przy tylko częściowym przejściu po wyniku.
memoize(iterable $iterable): IteratorAggregate
Tworzy wrapper wokół iteratora, który podczas iteracji cachuje jego klucze i wartości. Pozwala to wielokrotnie przechodzić po danych bez konieczności ponownego przechodzenia po pierwotnym źródle danych.
$iterator = /* ... */; // dane, po których nie da się iterować wielokrotnie
$memoized = Iterables::memoize($iterator);
// teraz możesz iterować po $memoized wiele razy bez utraty danych
Ta metoda przydaje się w sytuacjach, gdy potrzebujesz kilkakrotnie przejść po tym samym zestawie danych, ale pierwotny iterator nie pozwala na wielokrotną iterację albo ponowne przejście byłoby kosztowne (np. odczyt danych z bazy danych albo pliku).
repeatable(callable $factory): IteratorAggregate
Pozwala wielokrotnie iterować po obiektach, które normalnie tego nie obsługują, typowo po generatorach PHP. Metoda repeatable()
rozwiązuje ten problem elegancko: zamiast przekazywać sam iterator, przekazujesz funkcję, która go tworzy. Ta fabryka jest
potem automatycznie wywoływana przy każdej pętli iteracyjnej.
// zwykły generator, po którym nie da się iterować dwa razy
$generator = function () {
yield 'A';
yield 'B';
};
$iterator = Iterables::repeatable($generator);
foreach ($iterator as $v) echo $v; // wypisze: AB
foreach ($iterator as $v) echo $v; // wypisze: AB (generator uruchomił się ponownie)
Ta metoda jest alternatywą dla memoize() w sytuacjach, gdy pracujesz z dużymi ilościami
danych, bo repeatable() nie cachuje danych, lecz generuje je ponownie przy każdej iteracji.
some(iterable $iterable, callable $predicate): bool
Sprawdza, czy co najmniej jeden element iteratora spełnia warunek zdefiniowany w $predicate. Callback
$predicate ma sygnaturę function ($value, $key, iterable $iterable): bool i musi zwrócić
true dla co najmniej jednego elementu, aby metoda some() zwróciła true.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isEven = fn($value) => $value % 2 === 0;
$res = Iterables::some($iterator, $isEven); // true
Ta metoda przydaje się do szybkiego sprawdzenia, czy co najmniej jeden element kolekcji spełnia określony warunek, na przykład czy kolekcja zawiera co najmniej jedną liczbę parzystą.
Zobacz every().
toIterator(iterable $iterable): Iterator
Konwertuje dowolny obiekt iterowalny (tablicę, Traversable) na Iterator. Jeśli wejściem jest już Iterator, zwracany jest bez zmian.
$array = [1, 2, 3];
$iterator = Iterables::toIterator($array);
// teraz masz Iterator zamiast tablicy
Ta metoda przydaje się, gdy potrzebujesz mieć pewność, że dysponujesz Iteratorem, niezależnie od typu danych wejściowych. Bywa to pomocne przy tworzeniu funkcji pracujących z różnymi typami danych iterowalnych.