Итерирование множеств и мультимножеств

May 3, 2026 · View on GitHub

Вернуться к главной странице

Инструменты для операций над множествами и мультимножествами на итерируемых сущностях.


Distinct

Фильтрует коллекцию, выдавая только уникальные значения.

Set::distinct(iterable $data, bool $strict = true)

По умолчанию выполняет сравнение в режиме строгой типизации. Передайте значение false аргумента $strict, чтобы работать в режиме приведения типов.

use IterTools\Set;

$chessSet = ['rook', 'rook', 'knight', 'knight', 'bishop', 'bishop', 'king', 'queen', 'pawn', 'pawn', ... ];

foreach (Set::distinct($chessSet) as $chessPiece) {
    print($chessPiece);
}
// rook, knight, bishop, king, queen, pawn

$mixedTypes = [1, '1', 2, '2', 3];

foreach (Set::distinct($mixedTypes, false) as $datum) {
    print($datum);
}
// 1, 2, 3

Distinct By

Фильтрует коллекцию, возвращая только уникальные элементы согласно заданной функции сравнения.

Set::distinctBy(iterable $data, callable $compareBy)

use IterTools\Set;

$streetFighterConsoleReleases = [
    ['id' => '112233', 'name' => 'Street Fighter 3 3rd Strike', 'console' => 'Dreamcast'],
    ['id' => '223344', 'name' => 'Street Fighter 3 3rd Strike', 'console' => 'PS4'],
    ['id' => '334455', 'name' => 'Street Fighter 3 3rd Strike', 'console' => 'PS5'],
    ['id' => '445566', 'name' => 'Street Fighter VI', 'console' => 'PS4'],
    ['id' => '556677', 'name' => 'Street Fighter VI', 'console' => 'PS5'],
    ['id' => '667788', 'name' => 'Street Fighter VI', 'console' => 'PC'],
];
$compareBy = fn ($sfTitle) => $sfTitle['name'];

$uniqueTitles = [];
foreach (Set::distinctBy($streetFighterConsoleReleases, $compareBy) as $sfTitle) {
    $uniqueTitles[] = $sfTitle;
}

// Содержит одну запись для SF3 3rd Strike и одну для SFVI.

Distinct Adjacent

Удаляет только подряд идущие дубликаты в коллекции (поведение Unix uniq).

Set::distinctAdjacent(iterable $data)

  • Каждый элемент сравнивается строго (===) с предыдущим выданным элементом.
  • Не подряд идущие дубликаты сохраняются.
  • Работает с памятью O(1) — хранится только предыдущий элемент.
  • Исходные ключи отбрасываются; результат — список с последовательными целочисленными ключами.
use IterTools\Set;

$values = [1, 1, 2, 2, 3, 1, 1];

$result = [];
foreach (Set::distinctAdjacent($values) as $value) {
    $result[] = $value;
}
// [1, 2, 3, 1] — последняя 1 сохраняется, потому что не идёт подряд с более ранними
use IterTools\Set;

$logLines = ['error: timeout', 'error: timeout', 'error: timeout', 'info: ok', 'error: timeout'];

$collapsed = [];
foreach (Set::distinctAdjacent($logLines) as $line) {
    $collapsed[] = $line;
}
// ['error: timeout', 'info: ok', 'error: timeout']

См. также Stream::distinctAdjacent.

Distinct Adjacent By

Удаляет только подряд идущие дубликаты в коллекции, сравнивая значения, возвращаемые функцией ключа.

Set::distinctAdjacentBy(iterable $data, callable $keyFn)

  • Извлечённый ключ каждого элемента сравнивается строго (===) с ключом предыдущего элемента.
  • Не подряд идущие дубликаты по ключу сохраняются.
  • Работает с памятью O(1) и вызывает $keyFn ровно один раз на элемент.
  • Исходные ключи отбрасываются; результат — список с последовательными целочисленными ключами.
use IterTools\Set;

$words = ['apple', 'ant', 'banana', 'berry', 'apple'];

$firstLetterRuns = [];
foreach (Set::distinctAdjacentBy($words, fn ($s) => $s[0]) as $word) {
    $firstLetterRuns[] = $word;
}
// ['apple', 'banana', 'apple'] — первое слово в каждой серии с одинаковой первой буквой
use IterTools\Set;

$readings = [
    ['ts' => 60,  'v' => 1],
    ['ts' => 65,  'v' => 2],
    ['ts' => 119, 'v' => 3],
    ['ts' => 120, 'v' => 4],
    ['ts' => 121, 'v' => 5],
];
$minuteKey = fn ($r) => intdiv($r['ts'], 60);

$compressed = [];
foreach (Set::distinctAdjacentBy($readings, $minuteKey) as $reading) {
    $compressed[] = $reading;
}
// сохраняется только первое показание из каждой серии в той же минуте:
// [['ts' => 60, 'v' => 1], ['ts' => 120, 'v' => 4]]

См. также Stream::distinctAdjacentBy.

Intersection

Итерирует пересечение коллекций.

Set::intersection(iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила пересечения мультимножеств.

use IterTools\Set;

$chessPieces = ['rook', 'knight', 'bishop', 'queen', 'king', 'pawn'];
$shogiPieces = ['rook', 'knight', 'bishop' 'king', 'pawn', 'lance', 'gold general', 'silver general'];

foreach (Set::intersection($chessPieces, $shogiPieces) as $commonPiece) {
    print($commonPiece);
}
// rook, knight, bishop, king, pawn

Intersection Coercive

Итерирует пересечение коллекций в режиме приведения типов.

Set::intersectionCoercive(iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила пересечения мультимножеств.

use IterTools\Set;

$numbers  = [1, 2, 3, 4, 5];
$numerics = ['1', '2', 3];

foreach (Set::intersectionCoercive($numbers, $numerics) as $commonNumber) {
    print($commonNumber);
}
// 1, 2, 3

Partial Intersection

Итерирует M-частичное пересечение коллекций.

Set::partialIntersection(int $minIntersectionCount, iterable ...$iterables)

  • Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила пересечения мультимножеств.
  • Если $minIntersectionCount = 1, работают правила объединения мультимножеств.
use IterTools\Set;

$staticallyTyped    = ['c++', 'java', 'c#', 'go', 'haskell'];
$dynamicallyTyped   = ['php', 'python', 'javascript', 'typescript'];
$supportsInterfaces = ['php', 'java', 'c#', 'typescript'];

foreach (Set::partialIntersection(2, $staticallyTyped, $dynamicallyTyped, $supportsInterfaces) as $language) {
    print($language);
}
// c++, java, c#, go, php

Partial Intersection Coercive

Итерирует M-частичное пересечение коллекций в режиме приведения типов.

Set::partialIntersectionCoercive(int $minIntersectionCount, iterable ...$iterables)

  • Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила пересечения мультимножеств.
  • Если $minIntersectionCount = 1, работают правила объединения мультимножеств.
use IterTools\Set;

$set1 = [1, 2, 3],
$set2 = ['2', '3', 4, 5],
$set3 = [1, '2'],

foreach (Set::partialIntersectionCoercive(2, $set1, $set2, $set3) as $partiallyCommonNumber) {
    print($partiallyCommonNumber);
}
// 1, 2, 3

Difference

Итерирует разность коллекций. Возвращает элементы из первой коллекции, не входящие ни в одну из остальных.

Set::difference(iterable $a, iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила получения разности мультимножеств.

use IterTools\Set;

$a = [1, 2, 3, 4, 7];
$b = [2, 3, 5, 8];
$c = [1, 6, 9];

foreach (Set::difference($a, $b, $c) as $item) {
    print($item);
}
// 4, 7

Difference Coercive

Итерирует разность коллекций в режиме приведения типов.

Set::differenceCoercive(iterable $a, iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила получения разности мультимножеств.

use IterTools\Set;

$a = [1, 2, 3, 4, 7];
$b = ['1', 2, 3, 5, 8];

foreach (Set::differenceCoercive($a, $b) as $item) {
    print($item);
}
// 4, 7

Symmetric difference

Итерирует симметрическую разность коллекций.

Set::symmetricDifference(iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила получения разности мультимножеств.

use IterTools\Set;

$a = [1, 2, 3, 4, 7];
$b = ['1', 2, 3, 5, 8];
$c = [1, 2, 3, 6, 9];

foreach (Set::symmetricDifference($a, $b, $c) as $item) {
    print($item);
}
// 1, 4, 5, 6, 7, 8, 9

Symmetric difference Coercive

Итерирует симметрическую разность коллекций в режиме приведения типов.

Set::symmetricDifferenceCoercive(iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила получения разности мультимножеств.

use IterTools\Set;

$a = [1, 2, 3, 4, 7];
$b = ['1', 2, 3, 5, 8];
$c = [1, 2, 3, 6, 9];

foreach (Set::symmetricDifferenceCoercive($a, $b, $c) as $item) {
    print($item);
}
// 4, 5, 6, 7, 8, 9

Union

Итерирует объединение коллекций.

Set::union(iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила объединения мультимножеств.

use IterTools\Set;

$a = [1, 2, 3];
$b = [3, 4];
$c = [1, 2, 3, 6, 7];

foreach (Set::union($a, $b, $c) as $item) {
    print($item);
}
//1, 2, 3, 4, 6, 7

Union Coercive

Итерирует объединение коллекций в режиме приведения типов.

Set::unionCoercive(iterable ...$iterables)

Если хотя бы в одной коллекции встречаются повторяющиеся элементы, работают правила объединения мультимножеств.

use IterTools\Set;

$a = ['1', 2, 3];
$b = [3, 4];
$c = [1, 2, 3, 6, 7];

foreach (Set::unionCoercive($a, $b, $c) as $item) {
    print($item);
}
//1, 2, 3, 4, 6, 7

Duplicates

Отдаёт каждое дублирующееся значение по одному разу — в момент его второго появления.

Set::duplicates(iterable $data, bool $strict = true)

  • Параметр $strict соответствует семантике сравнения в Set::distinct.
  • Ключи исходной коллекции отбрасываются; ключи результата — последовательные, начиная с 0.
use IterTools\Set;

$data = [1, 2, 1, 1, 2, 3];

foreach (Set::duplicates($data) as $value) {
    print($value);
}
// 1, 2

Duplicates By

Отдаёт каждое значение, чей извлечённый ключ совпадает с уже встречавшимся, по одному разу — в момент второго появления такого ключа.

Set::duplicatesBy(iterable $data, callable $keyFn)

  • Отдаётся первое значение, чей ключ совпал; последующие совпадения по тому же ключу не отдаются.
  • Сравнение извлечённых ключей строгое.
  • Ключи исходной коллекции отбрасываются; ключи результата — последовательные, начиная с 0.
use IterTools\Set;

$users = [
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 2, 'name' => 'Bob'],
    ['id' => 1, 'name' => 'Alicia'],
    ['id' => 3, 'name' => 'Carol'],
];

foreach (Set::duplicatesBy($users, fn ($u) => $u['id']) as $duplicate) {
    print($duplicate['name']);
}
// Alicia