Перейти к содержимому
Теория · Функции для массивов · урок 7 из 8

Слияние, срезы и разбиение

Остались три задачи для персонала гардероба: собрать два гардероба в один, отдать часть вещей и развесить по крючкам вещи, которые принесли одной связкой.

Начнём со слияния. Если вещи висят на крючках с номерами, вещи второго гардероба вешают на следующие свободные крючки.

С именными бирками сложнее: что делать, если бирка 'color' есть в обоих гардеробах? У PHP на это два разных ответа:

PHP
<?php

$a = [1, 2];
$b = [3, 4];
echo json_encode(array_merge($a, $b)), "\n"; // [1,2,3,4]
echo json_encode([...$a, ...$b]), "\n"; // [1,2,3,4]
$defaults = ['color' => 'red', 'size' => 'M'];
$chosen = ['color' => 'blue'];
echo json_encode(array_merge($defaults, $chosen)), "\n"; // {"color":"blue","size":"M"}
echo json_encode($chosen + $defaults), "\n"; // {"color":"blue","size":"M"}
echo json_encode($defaults + $chosen), "\n"; // {"color":"red","size":"M"}

Три способа слить массивы

array_merge

array_merge($a, $b) берёт элементы $a, а за ними элементы $b. Целые ключи она раздаёт заново по порядку, поэтому два списка склеиваются в один. При повторе строкового ключа побеждает правый массив: 'blue' из $chosen заменяет 'red' из $defaults.

Оператор +

$x + $y добавляет к левому массиву только те ключи, которых в нём ещё нет. Совпавший ключ остаётся за левым массивом, поэтому $defaults + $chosen сохраняет 'red'. Отсюда удобный приём: $chosen + $defaults — выбор пользователя, дополненный настройками по умолчанию.

Распаковка ...

Запись [...$a, ...$b] высыпает элементы обоих массивов в новый список, как array_merge. Со строковыми ключами она работает так же: с PHP 8.1 при повторе ключа побеждает правый массив.

Чем array_merge отличается от +, часто спрашивают на собеседовании. Ответ:

  • array_merge нумерует целые ключи заново и отдаёт повторный строковый ключ правому массиву;
  • + сохраняет все ключи левого массива и только добавляет недостающие.

Срез и вырезание

Срез — копия части массива. Его возвращает array_slice, а array_splice не копирует часть, а вырезает её:

PHP
$queue = ['ann', 'bob', 'cat', 'dan'];
$part = array_slice($queue, 1, 2);
echo implode(' ', $part), ' | ', count($queue), "\n"; // bob cat | 4
$cut = array_splice($queue, 1, 2);
echo implode(' ', $cut), ' | ', implode(' ', $queue), "\n"; // bob cat | ann dan
array_splice($queue, 1, 0, ['eve']);
echo implode(' ', $queue), "\n"; // ann eve dan

array_slice копирует

array_slice($queue, 1, 2) возвращает новый массив: начать с позиции 1, считая с нуля, и взять 2 элемента. Третий аргумент — сколько взять, а не где закончить. Исходный массив не меняется: в $queue по-прежнему четыре имени.

array_splice вырезает

array_splice принимает массив по ссылке, как sort. Она вырезает кусок из самого $queue и возвращает вырезанное, а целые ключи оставшихся элементов нумерует заново.

Четвёртым аргументом можно передать, что вставить на освободившееся место. С длиной 0 она ничего не удаляет, а только вставляет: так 'eve' встала между 'ann' и 'dan'.

Здесь картинка с гардеробом неточна. Настоящий гардеробщик не может выдать вещь и при этом оставить её на крючке. А array_slice делает именно это: отдаёт копии, а вещи остаются на месте.

Из строки в массив и обратно

Строка с разделителями — та самая связка. explode развязывает её и развешивает куски по крючкам, а склеивает их обратно уже знакомая вам implode:

PHP
$csv = 'tea,milk,,bread';
$items = explode(',', $csv);
echo count($items), "\n"; // 4
var_dump($items[2]); // string(0) ""
echo implode(' + ', $items), "\n"; // tea + milk +  + bread
echo implode(' ', str_split('abcdef', 2)), "\n"; // ab cd ef

Давайте разберём по строкам:

  1. explode(',', $csv) режет строку по каждой запятой. Первым аргументом идёт разделитель, вторым — строка.
  2. Между двумя запятыми подряд ничего нет, и explode возвращает этот пустой кусок отдельным элементом. Поэтому частей четыре, а $items[2] — пустая строка.
  3. implode склеивает части обратно, и на месте пустого куска видны два пробела подряд.
  4. str_split('abcdef', 2) режет строку на куски по два байта; у латинских букв байт равен символу, поэтому здесь выходит ab cd ef. Кириллическую букву она может разрезать пополам: для кириллицы есть mb_str_split.

Частая ошибка

Два списка сложили через +:

PHP
$week = ['mon', 'tue'];
$weekend = ['sat', 'sun'];
$days = $week + $weekend; // ✗
echo implode(' ', $days), "\n";

Программа печатает mon tue, и PHP не выдаёт никаких сообщений. У обоих списков ключи 0 и 1, а + добавляет только ключи, которых слева нет: новых не оказалось. Исправление — array_merge($week, $weekend) или [...$week, ...$weekend]: оба дают mon tue sat sun.

Главное

  • array_merge и [...$a, ...$b] дописывают элементы и нумеруют целые ключи заново; при повторе строкового ключа побеждает правый массив.
  • + сохраняет все ключи левого массива и добавляет только недостающие, поэтому два списка через + не склеиваются.
  • array_slice возвращает копию куска и не трогает источник, а array_splice вырезает кусок из самого массива и возвращает вырезанное.
  • explode режет строку по разделителю и сохраняет пустые куски, а implode склеивает массив обратно.