Списки и массивы в PHP: различные методы обработки данных

Раздел: Веб-разработка -> Работа с массивами в PHP

В PHP массивы являются универсальной структурой данных, которая может представлять как упорядоченные списки (числовые индексы), так и ассоциативные коллекции. При работе со списками (последовательностями элементов) обычно используются числовые массивы, где ключи начинаются с нуля и возрастают на единицу. Эта статья рассматривает основные приёмы создания, модификации и анализа списков на основе встроенных функций и конструкций языка.

Основные подходы к работе со списками в PHP

Наиболее эффективное решение для массового преобразования элементов списка - использование встроенной функции array_map. Она применяет заданный callback к каждому элементу и возвращает новый массив, не изменяя исходный. Такой подход оптимизирован на уровне ядра PHP и даёт лаконичный код без явных циклов.


$numbers = [1, 2, 3, 4, 5];
$squares = array_map(fn($n) => $n * $n, $numbers);
print_r($squares);
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => 16
    [4] => 25
)

Возможная проблема:

Если callback ожидает более одного аргумента (например, array_map с несколькими массивами), а передан только один, может возникнуть ошибка несоответствия сигнатуры. Рекомендуется использовать стрелочные функции или явные анонимные функции с одним параметром. Для сохранения ключей при использовании array_map с ассоциативным списком придётся дополнительно применить array_combine.

Цель и случаи использования:

Применяется для однотипного преобразования элементов (умножение, форматирование, извлечение поля) без побочных эффектов. Идеально подходит для конвейерной обработки данных.

Как обойти все элементы упорядодоченного списка?

Простейший способ - конструкция foreach. Она безопасна для массива и автоматически перебирает все элементы.


$colors = ['red', 'green', 'blue'];
foreach ($colors as $index => $color) {
    echo "$index: $color\n";
}
0: red
1: green
2: blue

Типичные ошибки:

Изменение массива внутри foreach (добавление или удаление элементов) может привести к непредсказуемому поведению. Для модификации лучше использовать цикл for с счётчиком или функцию array_walk. Также не рекомендуется полагаться на внутренний указатель массива после выхода из цикла.

Цель:

Перебор списка для чтения или вывода данных без изменения структуры.

Как выделить элементы списка по критерию?

Функция array_filter возвращает элементы, для которых callback возвращает true. Ключи сохраняются, поэтому для получения числового индекса после фильтрации применяют array_values.


$numbers = [10, 15, 20, 25, 30];
$even = array_filter($numbers, fn($v) => $v % 2 === 0);
$evenIndexed = array_values($even);
print_r($evenIndexed);
Array
(
    [0] => 10
    [1] => 20
    [2] => 30
)

Проблема:

По умолчанию array_filter удаляет элементы, равные false, без callback. Если оставить callback пустым, будут удалены 0, '', null, false, что не всегда ожидаемо. Лучше всегда передавать явную функцию.

Цель:

Отбор подмножества элементов для последующей обработки или анализа.

Как присвоить значения первых элементов списка отдельным переменным?

Конструкция list() (или сокращённый синтаксис [...]) позволяет деструктуризировать массив в переменные.


$data = ['Alice', 28, 'designer'];
list($name, $age) = $data;
echo "Name: $name, Age: $age\n";
Name: Alice, Age: 28

Ошибка:

Если в массиве меньше элементов, чем переменных, будет сгенерировано предупреждение Undefined array key. Стоит проверять длину массива функцией count перед деструктуризацией или использовать значения по умолчанию через array_pad.

Цель:

Быстрое извлечение первых элементов (например, при разборе кортежа из базы данных или файла).

Как создать фиксированный список для экономии памяти?

Класс SplFixedArray создаёт массив строго заданной длины, что уменьшает накладные расходы по сравнению с обычным array. Индексация только числовая.


$fixed = new SplFixedArray(3);
$fixed[0] = 'PHP';
$fixed[1] = 'JS';
$fixed[2] = 'Python';
foreach ($fixed as $lang) {
    echo "$lang\n";
}
PHP
JS
Python

Проблемы:

Нельзя изменить размер после создания (кроме setSize(), но это создаёт новый объект). Попытка обратиться к несуществующему индексу вызывает исключение RuntimeException. С точки зрения API отличается от обычного массива - не поддерживает функции array_* напрямую, требуется преобразование через toArray().

Цель:

Списки известного размера, где критична память (например, большие наборы векторов в математических расчётах).

Как вычислить общее значение на основе всех элементов списка?

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


$prices = [100, 200, 150];
$total = array_reduce($prices, fn($carry, $item) => $carry + $item, 0);
echo "Total: $total\n";
Total: 450

Типичная ошибка:

Забыть указать начальное значение для $carry (третий аргумент). Если массив пуст, без начального значения будет выброшено ValueError в PHP 8+. Всегда передавайте начальное значение, соответствующее логике (0 для сумм, 1 для произведений, '' для конкатенации).

Цель:

Агрегация данных (суммирование, поиск максимума, построение строки).

Продвинутые примеры работы со списками

В этом разделе представлены менее распространённые, но полезные приёмы обработки списков в PHP. Каждый пример снабжён кодом и выводом.

Пример 1. Сохранение ключей при преобразовании с array_map

Когда исходный список ассоциативный, array_map возвращает массив с числовыми индексами. Чтобы сохранить ключи, можно использовать array_combine в комбинации с array_map:

Пример

$items = ['a' => 10, 'b' => 20, 'c' => 30];
$doubled = array_combine(
    array_keys($items),
    array_map(fn($v) => $v * 2, $items)
);
print_r($doubled);
Array
(
    [a] => 20
    [b] => 40
    [c] => 60
)

Пояснение:

Функция array_keys извлекает исходные ключи, а array_combine объединяет их с преобразованными значениями.

Пример 2. Объединение списков с сохранением перезаписи

Оператор + для массивов объединяет, но не перезаписывает существующие ключи. Для полного слияния с перезаписью используют array_merge.

Пример

$list1 = [0 => 'apple', 1 => 'banana'];
$list2 = [1 => 'cherry', 2 => 'date'];
$merged = array_merge($list1, $list2);
print_r($merged);
Array
(
    [0] => apple
    [1] => cherry
    [2] => date
)

Пояснение:

array_merge переиндексирует числовые ключи, поэтому ключ 1 ('banana') заменён на 'cherry'. Для сохранения старых ключей при объединении ассоциативных массивов используют оператор +.

Пример 3. Сортировка пользовательским критерием

Функция usort сортирует массив, используя callback сравнения. Это удобно для сортировки списка объектов или сложных структур.

Пример

$users = [
    ['name' => 'John', 'age' => 25],
    ['name' => 'Anna', 'age' => 20],
    ['name' => 'Bob', 'age' => 30],
];
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);
print_r($users);
Array
(
    [0] => Array ([name] => Anna, [age] => 20)
    [1] => Array ([name] => John, [age] => 25)
    [2] => Array ([name] => Bob, [age] => 30)
)

Пояснение:

Космический оператор <=> возвращает -1, 0 или 1, что идеально подходит для функции сравнения. Массив сортируется по возрастанию возраста.

Пример 4. Разбиение списка на части

Функция array_chunk делит список на подмассивы заданного размера.

Пример

$nums = [1, 2, 3, 4, 5, 6, 7];
$chunks = array_chunk($nums, 3);
print_r($chunks);
Array
(
    [0] => Array ([0] => 1, [1] => 2, [2] => 3)
    [1] => Array ([0] => 4, [1] => 5, [2] => 6)
    [2] => Array ([0] => 7)
)

Пояснение:

Последний чанк может быть меньше указанного размера. Вторым параметром можно передать true для сохранения ключей из исходного списка.

Пример 5. Работа с очередью через SplQueue

Класс SplQueue (реализация очереди на основе двусвязного списка) оптимизирует операции enqueue (добавление) и dequeue (извлечение).

Пример

$queue = new SplQueue();
$queue->enqueue('first');
$queue->enqueue('second');
$queue->enqueue('third');
echo $queue->dequeue() . PHP_EOL;  // first
echo $queue->dequeue() . PHP_EOL;  // second
first
second

Пояснение:

SplQueue поддерживает FIFO (первым пришёл - первым ушёл) и не требует переиндексации, в отличие от array_shift.

Пример 6. Рекурсивная обработка многомерного списка

Для применения функции ко всем элементам вложенных массивов можно написать рекурсивную обёртку над array_map.

Пример

function array_map_recursive(callable $callback, array $array): array {
    $result = [];
    foreach ($array as $key => $value) {
        $result[$key] = is_array($value)
            ? array_map_recursive($callback, $value)
            : $callback($value);
    }
    return $result;
}

$deep = [['a', 'b'], ['c', ['d', 'e']]];
$upper = array_map_recursive('strtoupper', $deep);
print_r($upper);
Array
(
    [0] => Array ([0] => A, [1] => B)
    [1] => Array ([0] => C, [1] => Array ([0] => D, [1] => E))
)

Пояснение:

Рекурсивный подход позволяет обрабатывать деревья любой глубины. Альтернатива - использование array_walk_recursive, но он не возвращает новый массив.

Списки и массивы в PHP - comments

En
Php списки массива (php)