Arbeiten mit Iteratoren
***********************
.[perex]{data-version:4.0.4}
[api:Nette\Utils\Iterables] ist eine statische Klasse mit Funktionen für die Arbeit mit Iteratoren. Ihr Gegenstück für Arrays ist [Nette\Utils\Arrays|arrays].
Installation:
```shell
composer require nette/utils
```
Alle Beispiele setzen voraus, dass dieser Alias angelegt wurde:
```php
use Nette\Utils\Iterables;
```
contains(iterable $iterable, $value): bool .[method]
----------------------------------------------------
Sucht einen angegebenen Wert in einem Iterator. Für die Prüfung auf Übereinstimmung wird ein strikter Vergleich (`===`) verwendet. Gibt `true` zurück, wenn der Wert gefunden wird, sonst `false`.
```php
Iterables::contains(new ArrayIterator([1, 2, 3]), 1); // true
Iterables::contains(new ArrayIterator([1, 2, 3]), '1'); // false
```
Diese Methode ist nützlich, wenn Sie schnell feststellen müssen, ob ein bestimmter Wert in einem Iterator vorkommt, ohne alle Elemente von Hand zu durchlaufen.
containsKey(iterable $iterable, $key): bool .[method]
-----------------------------------------------------
Sucht einen angegebenen Schlüssel in einem Iterator. Für die Prüfung auf Übereinstimmung wird ein strikter Vergleich (`===`) verwendet. Gibt `true` zurück, wenn der Schlüssel gefunden wird, sonst `false`.
```php
Iterables::containsKey(new ArrayIterator([1, 2, 3]), 0); // true
Iterables::containsKey(new ArrayIterator([1, 2, 3]), 4); // false
```
every(iterable $iterable, callable $predicate): bool .[method]
--------------------------------------------------------------
Prüft, ob alle Elemente des Iterators die in `$predicate` definierte Bedingung erfüllen. Der Callback `$predicate` hat die Signatur `function ($value, $key, iterable $iterable): bool` und muss für jedes Element `true` zurückgeben, damit die Methode `every()` `true` zurückgibt.
```php
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isBelowThreshold = fn($value) => $value < 40;
$res = Iterables::every($iterator, $isBelowThreshold); // true
```
Diese Methode ist nützlich, um zu prüfen, ob alle Elemente einer Sammlung eine bestimmte Bedingung erfüllen, etwa ob alle Zahlen kleiner als ein bestimmter Wert sind.
filter(iterable $iterable, callable $predicate): Generator .[method]
--------------------------------------------------------------------
Erzeugt einen neuen Iterator, der nur die Elemente des ursprünglichen Iterators enthält, die die in `$predicate` definierte Bedingung erfüllen. Der Callback `$predicate` hat die Signatur `function ($value, $key, iterable $iterable): bool` und muss für die Elemente `true` zurückgeben, die erhalten bleiben sollen.
```php
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::filter($iterator, fn($v) => $v < 3);
// 1, 2
```
Die Methode verwendet einen Generator, das Filtern geschieht also nach und nach, während Sie das Ergebnis durchlaufen. Das ist speicherschonend und erlaubt die Verarbeitung sehr großer Sammlungen. Wenn Sie nicht alle Elemente des resultierenden Iterators durchlaufen, sparen Sie Rechenleistung, weil nicht alle Elemente des ursprünglichen Iterators verarbeitet werden.
first(iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed .[method]
-------------------------------------------------------------------------------------------
Gibt das erste Element des Iterators zurück. Ist `$predicate` angegeben, gibt sie das erste Element zurück, das die Bedingung erfüllt. Der Callback `$predicate` hat die Signatur `function ($value, $key, iterable $iterable): bool`. Wird kein passendes Element gefunden, wird der Callback `$else` aufgerufen (sofern angegeben) und sein Ergebnis zurückgegeben. Ist `$else` nicht angegeben, wird `null` zurückgegeben.
```php
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
```
Diese Methode ist nützlich, wenn Sie schnell das erste Element einer Sammlung oder das erste Element mit einer bestimmten Eigenschaft brauchen, ohne die gesamte Sammlung von Hand zu durchlaufen.
firstKey(iterable $iterable, ?callable $predicate=null, ?callable $else=null): mixed .[method]
----------------------------------------------------------------------------------------------
Gibt den Schlüssel des ersten Elements des Iterators zurück. Ist `$predicate` angegeben, gibt sie den Schlüssel des ersten Elements zurück, das die Bedingung erfüllt. Der Callback `$predicate` hat die Signatur `function ($value, $key, iterable $iterable): bool`. Wird kein passendes Element gefunden, wird der Callback `$else` aufgerufen (sofern angegeben) und sein Ergebnis zurückgegeben. Ist `$else` nicht angegeben, wird `null` zurückgegeben.
```php
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 .[method]
-------------------------------------------------------------------
Erzeugt einen neuen Iterator, indem der Callback `$transformer` auf jedes Element des ursprünglichen Iterators angewendet wird. Der Callback `$transformer` hat die Signatur `function ($value, $key, iterable $iterable): mixed`, und sein Rückgabewert dient als neuer Wert des Elements.
```php
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::map($iterator, fn($v) => $v * 2);
// 2, 4, 6
```
Die Methode verwendet einen Generator, die Transformation geschieht also nach und nach, während Sie das Ergebnis durchlaufen. Das ist speicherschonend und erlaubt die Verarbeitung sehr großer Sammlungen. Wenn Sie nicht alle Elemente des resultierenden Iterators durchlaufen, sparen Sie Rechenleistung, weil nicht alle Elemente des ursprünglichen Iterators verarbeitet werden.
mapWithKeys(iterable $iterable, callable $transformer): Generator .[method]
---------------------------------------------------------------------------
Erzeugt einen neuen Iterator, indem die Werte und Schlüssel des ursprünglichen Iterators transformiert werden. Der Callback `$transformer` hat die Signatur `function ($value, $key, iterable $iterable): ?array{$newKey, $newValue}`. Gibt `$transformer` den Wert `null` zurück, wird das Element übersprungen. Bei den behaltenen Elementen dient das erste Element des zurückgegebenen Arrays als neuer Schlüssel und das zweite als neuer Wert.
```php
$iterator = new ArrayIterator(['a' => 1, 'b' => 2]);
$iterator = Iterables::mapWithKeys($iterator, fn($v, $k) => $v > 1 ? [$v * 2, strtoupper($k)] : null);
// [4 => 'B']
```
Wie `map()` verwendet auch diese Methode einen Generator, verarbeitet also nach und nach und schont den Speicher. So lässt sich mit großen Sammlungen arbeiten und Rechenleistung sparen, wenn Sie das Ergebnis nur teilweise durchlaufen.
memoize(iterable $iterable): IteratorAggregate .[method]
--------------------------------------------------------
Erzeugt einen Wrapper um einen Iterator, der dessen Schlüssel und Werte während des Durchlaufs zwischenspeichert. Das erlaubt es, die Daten mehrfach zu durchlaufen, ohne die ursprüngliche Datenquelle erneut zu durchlaufen.
```php
$iterator = /* ... */; // Daten, die sich nicht mehrfach durchlaufen lassen
$memoized = Iterables::memoize($iterator);
// Jetzt lässt sich $memoized mehrfach durchlaufen, ohne Daten zu verlieren
```
Diese Methode ist in Situationen nützlich, in denen Sie dieselbe Datenmenge mehrfach durchlaufen müssen, der ursprüngliche Iterator aber kein wiederholtes Durchlaufen erlaubt oder ein erneuter Durchlauf teuer wäre (etwa beim Lesen von Daten aus einer Datenbank oder Datei).
repeatable(callable $factory): IteratorAggregate .[method]{data-version:4.0.10}
-------------------------------------------------------------------------------
Erlaubt das wiederholte Durchlaufen von Objekten, die das sonst nicht unterstützen, typischerweise von [PHP-Generatoren |https://www.php.net/manual/en/language.generators.overview.php]. Die Methode `repeatable()` löst dieses Problem elegant: Statt des Iterators selbst übergeben Sie eine Funktion, die ihn erzeugt. Diese Factory wird dann bei jedem Durchlauf automatisch aufgerufen.
```php
// Ein üblicher Generator, der sich nicht zweimal durchlaufen lässt
$generator = function () {
yield 'A';
yield 'B';
};
$iterator = Iterables::repeatable($generator);
foreach ($iterator as $v) echo $v; // Gibt aus: AB
foreach ($iterator as $v) echo $v; // Gibt aus: AB (der Generator lief erneut)
```
Diese Methode ist eine Alternative zu [#memoize()] in Situationen, in denen Sie mit **großen Datenmengen** arbeiten, denn `repeatable()` speichert die Daten nicht zwischen, sondern erzeugt sie bei jedem Durchlauf neu.
some(iterable $iterable, callable $predicate): bool .[method]
-------------------------------------------------------------
Prüft, ob mindestens ein Element des Iterators die in `$predicate` definierte Bedingung erfüllt. Der Callback `$predicate` hat die Signatur `function ($value, $key, iterable $iterable): bool` und muss für mindestens ein Element `true` zurückgeben, damit die Methode `some()` `true` zurückgibt.
```php
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isEven = fn($value) => $value % 2 === 0;
$res = Iterables::some($iterator, $isEven); // true
```
Diese Methode ist nützlich, um schnell zu prüfen, ob mindestens ein Element einer Sammlung eine bestimmte Bedingung erfüllt, etwa ob die Sammlung mindestens eine gerade Zahl enthält.
Siehe [#every()].
toIterator(iterable $iterable): Iterator .[method]
--------------------------------------------------
Wandelt ein beliebiges iterierbares Objekt (Array, Traversable) in einen Iterator um. Ist die Eingabe bereits ein Iterator, wird sie unverändert zurückgegeben.
```php
$array = [1, 2, 3];
$iterator = Iterables::toIterator($array);
// Jetzt haben Sie statt eines Arrays einen Iterator
```
Diese Methode ist nützlich, wenn Sie sicherstellen müssen, dass Ihnen ein Iterator zur Verfügung steht, unabhängig vom Typ der Eingabedaten. Das kann beim Schreiben von Funktionen helfen, die mit verschiedenen Arten iterierbarer Daten arbeiten.
Arbeiten mit Iteratoren
Nette\Utils\Iterables ist eine statische Klasse mit
Funktionen für die Arbeit mit Iteratoren. Ihr Gegenstück für Arrays ist Nette\Utils\Arrays.
Installation:
composer require nette/utils
Alle Beispiele setzen voraus, dass dieser Alias angelegt wurde:
use Nette\Utils\Iterables;
contains (iterable $iterable, $value): bool
Sucht einen angegebenen Wert in einem Iterator. Für die Prüfung auf Übereinstimmung wird ein strikter Vergleich
(===) verwendet. Gibt true zurück, wenn der Wert gefunden wird, sonst false.
Iterables::contains(new ArrayIterator([1, 2, 3]), 1); // true
Iterables::contains(new ArrayIterator([1, 2, 3]), '1'); // false
Diese Methode ist nützlich, wenn Sie schnell feststellen müssen, ob ein bestimmter Wert in einem Iterator vorkommt, ohne alle
Elemente von Hand zu durchlaufen.
containsKey (iterable $iterable,
$key): bool
Sucht einen angegebenen Schlüssel in einem Iterator. Für die Prüfung auf Übereinstimmung wird ein strikter Vergleich
(===) verwendet. Gibt true zurück, wenn der Schlüssel gefunden wird, sonst 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
Prüft, ob alle Elemente des Iterators die in $predicate definierte Bedingung erfüllen. Der Callback
$predicate hat die Signatur function ($value, $key, iterable $iterable): bool und muss für jedes
Element true zurückgeben, damit die Methode every() true zurückgibt.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isBelowThreshold = fn($value) => $value < 40;
$res = Iterables::every($iterator, $isBelowThreshold); // true
Diese Methode ist nützlich, um zu prüfen, ob alle Elemente einer Sammlung eine bestimmte Bedingung erfüllen, etwa ob alle
Zahlen kleiner als ein bestimmter Wert sind.
filter (iterable $iterable, callable
$predicate): Generator
Erzeugt einen neuen Iterator, der nur die Elemente des ursprünglichen Iterators enthält, die die in $predicate
definierte Bedingung erfüllen. Der Callback $predicate hat die Signatur
function ($value, $key, iterable $iterable): bool und muss für die Elemente true zurückgeben, die
erhalten bleiben sollen.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::filter($iterator, fn($v) => $v < 3);
// 1, 2
Die Methode verwendet einen Generator, das Filtern geschieht also nach und nach, während Sie das Ergebnis durchlaufen. Das ist
speicherschonend und erlaubt die Verarbeitung sehr großer Sammlungen. Wenn Sie nicht alle Elemente des resultierenden Iterators
durchlaufen, sparen Sie Rechenleistung, weil nicht alle Elemente des ursprünglichen Iterators verarbeitet werden.
first (iterable $iterable, ?callable
$predicate=null, ?callable $else=null): mixed
Gibt das erste Element des Iterators zurück. Ist $predicate angegeben, gibt sie das erste Element zurück, das
die Bedingung erfüllt. Der Callback $predicate hat die Signatur
function ($value, $key, iterable $iterable): bool. Wird kein passendes Element gefunden, wird der Callback
$else aufgerufen (sofern angegeben) und sein Ergebnis zurückgegeben. Ist $else nicht angegeben, wird
null zurückgegeben.
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
Diese Methode ist nützlich, wenn Sie schnell das erste Element einer Sammlung oder das erste Element mit einer bestimmten
Eigenschaft brauchen, ohne die gesamte Sammlung von Hand zu durchlaufen.
firstKey (iterable $iterable, ?callable
$predicate=null, ?callable $else=null): mixed
Gibt den Schlüssel des ersten Elements des Iterators zurück. Ist $predicate angegeben, gibt sie den Schlüssel
des ersten Elements zurück, das die Bedingung erfüllt. Der Callback $predicate hat die Signatur
function ($value, $key, iterable $iterable): bool. Wird kein passendes Element gefunden, wird der Callback
$else aufgerufen (sofern angegeben) und sein Ergebnis zurückgegeben. Ist $else nicht angegeben, wird
null zurückgegeben.
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
Erzeugt einen neuen Iterator, indem der Callback $transformer auf jedes Element des ursprünglichen Iterators
angewendet wird. Der Callback $transformer hat die Signatur
function ($value, $key, iterable $iterable): mixed, und sein Rückgabewert dient als neuer Wert des Elements.
$iterator = new ArrayIterator([1, 2, 3]);
$iterator = Iterables::map($iterator, fn($v) => $v * 2);
// 2, 4, 6
Die Methode verwendet einen Generator, die Transformation geschieht also nach und nach, während Sie das Ergebnis durchlaufen.
Das ist speicherschonend und erlaubt die Verarbeitung sehr großer Sammlungen. Wenn Sie nicht alle Elemente des resultierenden
Iterators durchlaufen, sparen Sie Rechenleistung, weil nicht alle Elemente des ursprünglichen Iterators verarbeitet werden.
mapWithKeys (iterable $iterable, callable
$transformer): Generator
Erzeugt einen neuen Iterator, indem die Werte und Schlüssel des ursprünglichen Iterators transformiert werden. Der Callback
$transformer hat die Signatur function ($value, $key, iterable $iterable): ?array{$newKey, $newValue}.
Gibt $transformer den Wert null zurück, wird das Element übersprungen. Bei den behaltenen Elementen
dient das erste Element des zurückgegebenen Arrays als neuer Schlüssel und das zweite als neuer Wert.
$iterator = new ArrayIterator(['a' => 1, 'b' => 2]);
$iterator = Iterables::mapWithKeys($iterator, fn($v, $k) => $v > 1 ? [$v * 2, strtoupper($k)] : null);
// [4 => 'B']
Wie map() verwendet auch diese Methode einen Generator, verarbeitet also nach und nach und schont den Speicher. So
lässt sich mit großen Sammlungen arbeiten und Rechenleistung sparen, wenn Sie das Ergebnis nur teilweise durchlaufen.
memoize (iterable $iterable): IteratorAggregate
Erzeugt einen Wrapper um einen Iterator, der dessen Schlüssel und Werte während des Durchlaufs zwischenspeichert. Das erlaubt
es, die Daten mehrfach zu durchlaufen, ohne die ursprüngliche Datenquelle erneut zu durchlaufen.
$iterator = /* ... */; // Daten, die sich nicht mehrfach durchlaufen lassen
$memoized = Iterables::memoize($iterator);
// Jetzt lässt sich $memoized mehrfach durchlaufen, ohne Daten zu verlieren
Diese Methode ist in Situationen nützlich, in denen Sie dieselbe Datenmenge mehrfach durchlaufen müssen, der ursprüngliche
Iterator aber kein wiederholtes Durchlaufen erlaubt oder ein erneuter Durchlauf teuer wäre (etwa beim Lesen von Daten aus einer
Datenbank oder Datei).
repeatable (callable
$factory): IteratorAggregate
Erlaubt das wiederholte Durchlaufen von Objekten, die das sonst nicht unterstützen, typischerweise von PHP-Generatoren. Die Methode repeatable()
löst dieses Problem elegant: Statt des Iterators selbst übergeben Sie eine Funktion, die ihn erzeugt. Diese Factory wird dann
bei jedem Durchlauf automatisch aufgerufen.
// Ein üblicher Generator, der sich nicht zweimal durchlaufen lässt
$generator = function () {
yield 'A';
yield 'B';
};
$iterator = Iterables::repeatable($generator);
foreach ($iterator as $v) echo $v; // Gibt aus: AB
foreach ($iterator as $v) echo $v; // Gibt aus: AB (der Generator lief erneut)
Diese Methode ist eine Alternative zu memoize() in Situationen, in denen Sie mit großen
Datenmengen arbeiten, denn repeatable() speichert die Daten nicht zwischen, sondern erzeugt sie bei jedem
Durchlauf neu.
some (iterable $iterable, callable
$predicate): bool
Prüft, ob mindestens ein Element des Iterators die in $predicate definierte Bedingung erfüllt. Der Callback
$predicate hat die Signatur function ($value, $key, iterable $iterable): bool und muss für mindestens
ein Element true zurückgeben, damit die Methode some() true zurückgibt.
$iterator = new ArrayIterator([1, 30, 39, 29, 10, 13]);
$isEven = fn($value) => $value % 2 === 0;
$res = Iterables::some($iterator, $isEven); // true
Diese Methode ist nützlich, um schnell zu prüfen, ob mindestens ein Element einer Sammlung eine bestimmte Bedingung erfüllt,
etwa ob die Sammlung mindestens eine gerade Zahl enthält.
Siehe every().
toIterator (iterable $iterable): Iterator
Wandelt ein beliebiges iterierbares Objekt (Array, Traversable) in einen Iterator um. Ist die Eingabe bereits ein Iterator,
wird sie unverändert zurückgegeben.
$array = [1, 2, 3];
$iterator = Iterables::toIterator($array);
// Jetzt haben Sie statt eines Arrays einen Iterator
Diese Methode ist nützlich, wenn Sie sicherstellen müssen, dass Ihnen ein Iterator zur Verfügung steht, unabhängig vom Typ
der Eingabedaten. Das kann beim Schreiben von Funktionen helfen, die mit verschiedenen Arten iterierbarer Daten arbeiten.