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.