Итерирование нескольких коллекций
April 28, 2026 · View on GitHub
Инструменты для одновременного итерирования нескольких коллекций.
Chain
Последовательно итерирует коллекции поэлементно.
Multi::chain(iterable ...$iterables)
use IterTools\Multi;
$prequels = ['Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith'];
$originals = ['A New Hope', 'Empire Strikes Back', 'Return of the Jedi'];
foreach (Multi::chain($prequels, $originals) as $movie) {
print($movie);
}
// 'Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith', 'A New Hope', 'Empire Strikes Back', 'Return of the Jedi'
RoundRobin
Поочерёдно отдаёт по одному элементу из каждой коллекции, чередуя источники.
Multi::roundRobin(iterable ...$iterables)
В каждом раунде берётся по одному элементу из каждой коллекции, в которой ещё есть элементы;
исчерпавшаяся коллекция пропускается на последующих раундах. Итерирование завершается, когда
исчерпаны все коллекции. В отличие от zip, элементы возвращаются по одному, а не в виде кортежей.
Ключи исходных коллекций отбрасываются; результат имеет последовательные целочисленные ключи.
use IterTools\Multi;
$queueA = ['A', 'B', 'C'];
$queueB = ['D', 'E'];
$queueC = ['F', 'G', 'H'];
foreach (Multi::roundRobin($queueA, $queueB, $queueC) as $item) {
print($item);
}
// 'A', 'D', 'F', 'B', 'E', 'G', 'C', 'H'
Round-robin-планирование задач между очередями воркеров: задачи равномерно вычерпываются из всех очередей, пока все они не опустеют:
$workerOne = ['task-1', 'task-4', 'task-7'];
$workerTwo = ['task-2', 'task-5'];
$workerThree = ['task-3', 'task-6', 'task-8', 'task-9'];
$schedule = [];
foreach (Multi::roundRobin($workerOne, $workerTwo, $workerThree) as $task) {
$schedule[] = $task;
}
// ['task-1', 'task-2', 'task-3', 'task-4', 'task-5', 'task-6', 'task-7', 'task-8', 'task-9']
См. также: Stream::roundRobinWith.
Zip
Параллельно итерирует коллекции, пока не закончится самый короткий итератор.
Multi::zip(iterable ...$iterables)
use IterTools\Multi;
$languages = ['PHP', 'Python', 'Java', 'Go'];
$mascots = ['elephant', 'snake', 'bean', 'gopher'];
foreach (Multi::zip($languages, $mascots) as [$language, $mascot]) {
print("The {$language} language mascot is an {$mascot}.");
}
// The PHP language mascot is an elephant.
// ...
Может принимать больше двух коллекций на вход.
$names = ['Ryu', 'Ken', 'Chun Li', 'Guile'];
$countries = ['Japan', 'USA', 'China', 'USA'];
$signatureMoves = ['hadouken', 'shoryuken', 'spinning bird kick', 'sonic boom'];
foreach (Multi::zip($names, $countries, $signatureMoves) as [$name, $country, $signatureMove]) {
$streetFighter = new StreetFighter($name, $country, $signatureMove);
}
Примечание: для коллекций разных длин итерирование прекратится, когда закончится самая короткая коллекция.
ZipFilled
Параллельно итерирует коллекции, пока не закончится самый длинный итератор. Для закончившихся коллекций в качестве значения подставляет заданный филлер.
Multi::zipFilled(mixed $filler, iterable ...$iterables)
use IterTools\Multi;
$default = '?';
$letters = ['A', 'B'];
$numbers = [1, 2, 3];
foreach (Multi::zipFilled($default, $letters, $numbers) as [$letter, $number]) {
// ['A', 1], ['B', 2], ['?', 3]
}
ZipLongest
Параллельно итерирует коллекции, пока не закончится самый длинный итератор.
Multi::zipLongest(iterable ...$iterables)
Примечание: в случае итерирования коллекций разных длин для закончившихся коллекций в каждой следующей итерации будет подставляться значение null.
use IterTools\Multi;
$letters = ['A', 'B', 'C'];
$numbers = [1, 2];
foreach (Multi::zipLongest($letters, $numbers) as [$letter, $number]) {
// ['A', 1], ['B', 2], ['C', null]
}
ZipEqual
Параллельно итерирует коллекции одного размера, в случае разных размеров бросает исключение.
Бросает \LengthException, если длины коллекций окажутся неравны, как только закончится самая короткая.
Multi::zipEqual(iterable ...$iterables)
use IterTools\Multi;
$letters = ['A', 'B', 'C'];
$numbers = [1, 2, 3];
foreach (Multi::zipEqual($letters, $numbers) as [$letter, $number]) {
// ['A', 1], ['B', 2], ['C', 3]
}
Unzip
Транспонирует последовательность строк в столбцы — обратная операция к zip.
Multi::unzip(iterable $rows)
Возвращает по одному массиву на каждый столбец. Количество столбцов равно ширине самой короткой
строки — лишние ячейки более длинных строк отбрасываются, как и в Multi::zip. Для входа
однородной положительной ширины тождество Multi::zip(...Multi::unzip($rows)) выполняется.
Оно не распространяется на вход нулевой ширины (например, [[], []] даёт ноль столбцов, и
число строк теряется), а также на результат zipFilled / zipLongest — отсечение по самой
короткой строке отбросит дополненные хвостовые ячейки.
Несмотря на то, что метод возвращает \Generator, он не является ленивым в практическом смысле:
весь вход буферизуется до того, как будет отдан первый столбец, ведь столбец 0 нельзя выдать,
пока не известны первые ячейки всех строк. Расход памяти — O(N · W) для N строк ширины W.
Бросает \InvalidArgumentException, если хотя бы одна строка не является итерируемой, с
указанием её нулевого индекса.
Ключи строк и ключи внутри ячеек отбрасываются; столбцы возвращаются как списки с последовательными целочисленными ключами.
use IterTools\Multi;
$pairs = [[1, 'a'], [2, 'b'], [3, 'c']];
foreach (Multi::unzip($pairs) as $column) {
print_r($column);
}
// [1, 2, 3]
// ['a', 'b', 'c']
Разделение событий вида (timestamp, value) на два параллельных ряда:
$events = [
[1700000000, 12.5],
[1700000060, 13.1],
[1700000120, 12.9],
[1700000180, 13.4],
];
[$timestamps, $values] = \iterator_to_array(Multi::unzip($events), false);
// $timestamps === [1700000000, 1700000060, 1700000120, 1700000180]
// $values === [12.5, 13.1, 12.9, 13.4]
См. также: Stream::unzip.