Получение частей коллекции
Иногда нужен не весь массив, а только его часть: диапазон элементов, несколько первых или последних, куски одинакового размера или скользящее окно из соседей.
Основной инструмент для этого в PHP — array_slice. array_slice($words, $offset, $length) возвращает $length элементов, начиная с индекса $offset. Второе число — это количество элементов, а не конечный индекс. Если $length не указать, срез дойдёт до конца. Отрицательный $offset отсчитывается от конца, а отрицательный $length останавливается за столько элементов до конца.
array_slice($s, $i, $n)—$nэлементов, начиная с индекса$iarray_slice($s, 0, $n)— первые$nэлементовarray_slice($s, $i)— всё от индекса$iдо концаarray_slice($s, -$n)— последние$nэлементовarray_slice($s, 0, -$n)— всё, кроме последних$nэлементов
array_slice всегда создаёт новый массив и копирует в него выбранные элементы. Изменение копии не меняет исходный массив, а изменение исходного массива не меняет копию. В некоторых других языках та же операция даёт представление, разделяющее память с исходными данными, — в PHP этого не происходит никогда.
Для кусков одинакового размера в PHP есть вторая готовая функция — array_chunk. Для задач, с которыми ни одна из этих функций не справляется сама, в этом уроке используется цикл for или foreach и одна-две небольшие вспомогательные функции.
Большинство примеров на этой странице работают с одними и теми же данными: словами one two three four five six.
Срез
Срез — это выбор элементов на некоторых заданных позициях.
Для последовательности соседних позиций используйте array_slice: array_slice($words, 1, 3) даёт элементы с индексами 1, 2 и 3. Внимание: 3 — это длина, а не конечный индекс: начинаем с индекса 1 и берём 3 элемента.
array_slice не умеет пропускать элементы. Чтобы взять каждый второй элемент, используйте цикл for с $i += 2. Если позиции вы перечисляете сами, в своём порядке, то array_map по массиву индексов выберет элемент на каждой из них.
Срез диапазона
array_slice($words, 1, 3) начинает с индекса 1 и берёт 3 элемента: слова с индексами 1, 2 и 3. Результат — новый массив, поэтому его изменение не затронуло бы $words. Программа выводит two, three, four.
Вывод: PHP-скрипт
<?php
$words = ["one", "two", "three", "four", "five", "six"];
// смещение 1, длина 3: элементы с индексами 1, 2 и 3
$part = array_slice($words, 1, 3);
echo implode(", ", $part), "\n";
Срез с шагом
Чтобы взять каждый второй элемент, цикл for с $i += 2 проходит по индексам 0, 2 и 4 и собирает слова. $part[] = ... добавляет элемент в конец массива.
Вывод: PHP-скрипт
<?php
$words = ["one", "two", "three", "four", "five", "six"];
// берём каждый второй элемент с индекса 0 по индекс 4
$part = [];
for ($i = 0; $i <= 4; $i += 2) {
$part[] = $words[$i];
}
echo implode(", ", $part), "\n";
Срез по списку индексов
Позиции 3, 5 и 0 хранятся в массиве чисел. array_map берёт $words[$i] для каждой из них, поэтому результат получается в порядке индексов: four, six, one.
Вывод: PHP-скрипт
<?php
$words = ["one", "two", "three", "four", "five", "six"];
$indices = [3, 5, 0];
// выбираем элемент на каждой позиции, в этом же порядке
$part = array_map(fn($i) => $words[$i], $indices);
echo implode(", ", $part), "\n";
Take и Drop
Take означает оставить некоторое количество элементов с одного конца. Drop означает отбросить некоторое количество элементов с одного конца и оставить остальное.
В PHP и то и другое — вызовы array_slice. array_slice($words, 0, 3) берёт первые 3 элемента, а array_slice($words, -3) — последние 3, потому что отрицательное смещение отсчитывается от конца. array_slice($words, 1) отбрасывает первый элемент, а array_slice($words, 0, -5) отбрасывает последние 5, потому что отрицательная длина останавливается за столько элементов до конца.
Есть и условные варианты: takeWhile, takeLastWhile, dropWhile и dropLastWhile. Они не считают элементы. Они идут с одного конца и останавливаются на первом элементе, который не удовлетворяет условию. Встроенных версий в PHP нет, поэтому задачу решает небольшая вспомогательная функция с циклом foreach, а варианты с Last просто переворачивают массив с помощью array_reverse, вызывают ту же вспомогательную функцию и переворачивают результат обратно.
Взять первые или последние n элементов
array_slice($words, 0, 3) оставляет первые 3 слова. array_slice($words, -3) оставляет последние 3, потому что отрицательное смещение отсчитывается от конца.
Вывод: PHP-скрипт
<?php
$words = ["one", "two", "three", "four", "five", "six"];
$first3 = array_slice($words, 0, 3); // первые 3 элемента
$last3 = array_slice($words, -3); // последние 3 элемента
echo implode(", ", $first3), "\n";
echo implode(", ", $last3), "\n";
Отбросить первые или последние n элементов
array_slice($words, 1) пропускает первое слово и оставляет остальные. array_slice($words, 0, -5) останавливается за 5 элементов до конца, поэтому остаётся только one.
Вывод: PHP-скрипт
<?php
$words = ["one", "two", "three", "four", "five", "six"];
$withoutFirst = array_slice($words, 1); // отбросить первый элемент
$withoutLast5 = array_slice($words, 0, -5); // отбросить последние 5 элементов
echo implode(", ", $withoutFirst), "\n";
echo implode(", ", $withoutLast5), "\n";
Брать, пока выполняется условие
takeWhile оставляет слова с начала, пока не встретит слово, начинающееся на f. str_starts_with() проверяет начало строки. takeLastWhile оставляет слова с конца, пока не встретит three: она переворачивает массив с помощью array_reverse, вызывает takeWhile и переворачивает результат обратно.
Вывод: PHP-скрипт
<?php
// takeWhile оставляет элементы с начала, пока $keep отвечает «да», и затем останавливается.
function takeWhile(array $items, callable $keep): array {
$result = [];
foreach ($items as $item) {
if (!$keep($item)) {
break;
}
$result[] = $item;
}
return $result;
}
// takeLastWhile делает то же самое с конца: перевернуть, взять, перевернуть обратно.
function takeLastWhile(array $items, callable $keep): array {
return array_reverse(takeWhile(array_reverse($items), $keep));
}
$words = ["one", "two", "three", "four", "five", "six"];
$fromStart = takeWhile($words, fn($w) => !str_starts_with($w, "f"));
$fromEnd = takeLastWhile($words, fn($w) => $w !== "three");
echo implode(", ", $fromStart), "\n";
echo implode(", ", $fromEnd), "\n";
Отбрасывать, пока выполняется условие
dropWhile пропускает трёхбуквенные слова в начале и оставляет всё начиная с three. dropLastWhile пропускает слова с конца, пока они содержат букву i. str_contains() проверяет, содержит ли строка заданный фрагмент текста.
Вывод: PHP-скрипт
<?php
// dropWhile пропускает элементы с начала, пока $skip отвечает «да», и оставляет остальные.
function dropWhile(array $items, callable $skip): array {
$result = [];
$dropping = true;
foreach ($items as $item) {
if ($dropping && $skip($item)) {
continue;
}
$dropping = false;
$result[] = $item;
}
return $result;
}
// dropLastWhile делает то же самое с конца: перевернуть, отбросить, перевернуть обратно.
function dropLastWhile(array $items, callable $skip): array {
return array_reverse(dropWhile(array_reverse($items), $skip));
}
$words = ["one", "two", "three", "four", "five", "six"];
$fromStart = dropWhile($words, fn($w) => strlen($w) === 3);
$fromEnd = dropLastWhile($words, fn($w) => str_contains($w, "i"));
echo implode(", ", $fromStart), "\n";
echo implode(", ", $fromEnd), "\n";
Разбиение на части
Разбиение на части — это разрезание массива на куски одинакового размера. Последний кусок может оказаться короче, если элементы не делятся ровно.
В PHP это встроено: array_chunk($numbers, 3) возвращает массив массивов, и короткий последний кусок получается правильным сам по себе. Поскольку результат вложенный, json_encode выводит его так, что каждый кусок стоит в своих скобках: [[0,1,2],[3,4,5],…].
Чтобы сразу обработать каждый кусок, передайте части в array_map: имя встроенной функции, переданное строкой, например 'array_sum', работает не хуже стрелочной функции.
Разбить на части по 3
Числа от 0 до 13 разрезаются на куски по 3 одним вызовом array_chunk. В последнем куске всего 2 элемента: [12,13].
Вывод: PHP-скрипт
<?php
$numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
// array_chunk разрезает массив на куски по 3; последний кусок короче
$chunks = array_chunk($numbers, 3);
echo json_encode($chunks), "\n";
Просуммировать каждую часть
Те же части, но каждая суммируется встроенной функцией array_sum. Её имя передаётся в array_map строкой. Остаются только суммы: 3, 12, 21, 30, 25.
Вывод: PHP-скрипт
<?php
$numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
// суммируем каждую часть по 3 с помощью array_sum; остаются только суммы
$sums = array_map('array_sum', array_chunk($numbers, 3));
echo implode(", ", $sums), "\n";
Окна
Окно — это группа соседних элементов. При работе с окнами группа скользит по массиву и собираются все её положения. В отличие от частей, окна перекрываются.
Встроенной функции windowed в PHP нет. Это делает цикл for, который на каждой позиции вызывает array_slice($items, $i, $size), а небольшая вспомогательная функция windowed делает такой код многоразовым. С параметром $step окно сдвигается сразу на несколько позиций. Если $partial равен true, более короткое окно в конце сохраняется, а не отбрасывается.
Окно размера 2 со шагом 1 — то же самое, что объединение каждого элемента со следующим. В PHP array_map с null вместо функции склеивает два массива элемент за элементом: подайте ей массив без последнего элемента и массив без первого — и вы получите пары соседей.
Окна по 3
Окно содержит 3 слова и сдвигается каждый раз на 1 позицию. Цикл останавливается, когда полное окно больше не помещается, поэтому окон получается 3. Результат — массив массивов, выведенный с помощью json_encode.
Вывод: PHP-скрипт
<?php
// windowed возвращает все группы по $size элементов, сдвигаясь на одну позицию за раз.
function windowed(array $items, int $size): array {
$result = [];
for ($i = 0; $i + $size <= count($items); $i++) {
$result[] = array_slice($items, $i, $size);
}
return $result;
}
$words = ["one", "two", "three", "four", "five"];
echo json_encode(windowed($words, 3)), "\n";
Окна с шагом и неполные окна
Окно содержит 3 числа и сдвигается каждый раз на 2 позиции. Так как $partial равен true, последнее окно [9,10] сохраняется, хотя оно короткое.
Вывод: PHP-скрипт
<?php
// windowed возвращает группы по $size элементов, сдвигаясь каждый раз на $step позиций.
// При $partial = true более короткая группа в конце тоже сохраняется.
function windowed(array $items, int $size, int $step = 1, bool $partial = false): array {
$result = [];
for ($i = 0; $i < count($items); $i += $step) {
$window = array_slice($items, $i, $size);
if (count($window) < $size && !$partial) {
break;
}
$result[] = $window;
}
return $result;
}
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
echo json_encode(windowed($numbers, 3, 2, true)), "\n";
Просуммировать каждое окно
Каждое окно из 3 соседей вырезается с помощью array_slice и суммируется с помощью array_sum. Остаются только суммы: 6, 9, 12.
Вывод: PHP-скрипт
<?php
$numbers = [1, 2, 3, 4, 5];
// просуммировать каждое окно из 3 соседей
$sums = [];
for ($i = 0; $i + 3 <= count($numbers); $i++) {
$sums[] = array_sum(array_slice($numbers, $i, 3));
}
echo implode(", ", $sums), "\n";
Объединить каждый элемент со следующим
array_map(null, ...) объединяет каждое слово с его соседом: в первом массиве-аргументе нет последнего элемента, во втором — первого, поэтому массивы выстраиваются как соседи. Второй цикл сравнивает те же пары соседей и записывает, длиннее ли первое слово второго; json_encode выводит логические значения как true и false.
Вывод: PHP-скрипт
<?php
$letters = ["one", "two", "three", "four", "five"];
// array_map с null склеивает два массива элемент за элементом:
// каждый элемент объединяется с идущим за ним
$pairs = array_map(null, array_slice($letters, 0, -1), array_slice($letters, 1));
echo json_encode($pairs), "\n";
// тот же проход, но сравниваются длины соседей
$longer = [];
for ($i = 0; $i + 1 < count($letters); $i++) {
$longer[] = strlen($letters[$i]) > strlen($letters[$i + 1]);
}
echo json_encode($longer), "\n";