Многомерные массивы в PHP
Узнайте, как создавать и использовать многомерные массивы в PHP: доступ к элементам, перебор циклами и трансформация данных.
Многомерный массив в PHP — это массив, элементы которого сами являются массивами. Обычный (индексированный или ассоциативный) массив сопоставляет каждый ключ с одним значением, тогда как многомерный массив сопоставляет каждый ключ с целым вложенным массивом — что позволяет моделировать данные в виде таблиц, сеток, деревьев и сгруппированных записей.
В этой главе рассматривается создание двумерных и более глубоких массивов, чтение и обновление отдельных ячеек, перебор с помощью foreach, а также наиболее распространённые функции для их преобразования. Кроме того, здесь указаны типичные ошибки, с которыми сталкиваются разработчики при переходе от плоских массивов к вложенным.
Что такое многомерный массив?
Массив становится многомерным, когда одно из его значений само является массивом. Размерность (или глубина) — это количество операций индексирования, необходимых для достижения скалярного значения:
$a[0]достигает значения → одномерный$a[0][1]достигает значения → двумерный$a[0][1][2]достигает значения → трёхмерный, и так далее.
Двумерный массив часто представляют как таблицу: внешний массив содержит строки, а каждый внутренний массив содержит столбцы этой строки. Эта мысленная модель полезна, но помните, что массивы PHP — это упорядоченные отображения, а не жёсткие сетки — строки могут иметь разную длину, а ключи могут быть строками вместо 0, 1, 2. Ограничения на глубину нет, однако сильно вложенные данные обычно свидетельствуют о том, что лучше использовать объекты или базу данных.
Создание многомерного массива
Наиболее распространённый способ — вкладывать литералы массивов. Каждый внутренний массив является одним элементом внешнего массива.
Двумерный массив строк (сетка)
$grid = [
["value1", "value2", "value3"],
["value4", "value5", "value6"],
["value7", "value8", "value9"],
];В реальном коде внутренние массивы обычно ассоциативные, поэтому каждая строка читается как запись:
Список записей
$employees = [
["name" => "Ann", "title" => "Engineer", "salary" => 65000],
["name" => "Bob", "title" => "Designer", "salary" => 58000],
["name" => "Cara", "title" => "Manager", "salary" => 72000],
];Можно также строить массив поэтапно — это удобно, когда данные поступают из цикла или запроса к базе данных:
$matrix = [];
$matrix[0][0] = 1;
$matrix[0][1] = 2;
$matrix[1][0] = 3;
$matrix[1][1] = 4;
// $matrix is now [[1, 2], [3, 4]]Доступ к элементам
Добавьте по одному набору скобок на каждое измерение. Первый индекс выбирает внешний элемент (строку), второй — внутренний (столбец):
echo $grid[1][2]; // "value6" — row 1, column 2
echo $employees[2]["name"]; // "Cara"PHP использует нулевую индексацию, поэтому вторая строка — это [1], а третий столбец — [2]. Обращение к несуществующему ключу вызывает предупреждение и возвращает null; используйте isset() или оператор null-объединения для безопасного доступа:
$salary = $employees[5]["salary"] ?? 0; // no warning if the row is missingПеребор многомерного массива
Вложенные циклы foreach — идиоматический способ обхода двумерного массива: внешний цикл посещает каждую строку, внутренний — значения этой строки:
$employees = [
["name" => "Ann", "salary" => 65000],
["name" => "Bob", "salary" => 58000],
];
foreach ($employees as $row) {
foreach ($row as $key => $value) {
echo "$key: $value\n";
}
echo "---\n";
}Для табличных записей зачастую удобнее сразу деструктурировать внутренние ключи:
foreach ($employees as $emp) {
echo "{$emp['name']} earns {$emp['salary']}\n";
}
// Ann earns 65000
// Bob earns 58000Изменение элементов
Используйте полный путь индексов для обновления отдельной ячейки, а для добавления новой строки используйте [] или array_push():
$employees[0]["salary"] = 70000; // update one field
$employees[] = ["name" => "Dan", "salary" => 60000]; // add a row
array_pop($employees); // remove the last rowarray_pop() удаляет и возвращает последнюю строку, поэтому две операции выше взаимно отменяются.
Преобразование с помощью array_map и array_column
array_map() применяет функцию обратного вызова к каждому элементу. Чтобы преобразовать каждую ячейку, примените вложенный array_map к каждой строке:
Привести все значения двумерного массива к верхнему регистру
$grid = [["a", "b"], ["c", "d"]];
$upper = array_map(function ($row) {
return array_map("strtoupper", $row);
}, $grid);
// $upper is [["A", "B"], ["C", "D"]]Когда внутренние массивы являются записями, array_column() извлекает одно поле из каждой строки в плоский массив — идеально для получения одного столбца:
$employees = [
["name" => "Ann", "salary" => 65000],
["name" => "Bob", "salary" => 58000],
];
$names = array_column($employees, "name"); // ["Ann", "Bob"]
$total = array_sum(array_column($employees, "salary")); // 123000Типичные ошибки
- Смещение индексов на единицу.
$grid[1][2]— это вторая строка, третий столбец — оба индекса начинаются с нуля. - Путаница порядка индексов.
$employees[0]["name"]работает;$employees["name"][0]— нет, потому что внешний массив индексируется целыми числами. - Несуществующие ключи. Обращение к отсутствующей строке или столбцу генерирует предупреждение и возвращает
null. Защищайтесь с помощьюisset()или?? default. - Функции, требующие рекурсии. Обычный
count($arr)считает только верхний уровень — используйтеcount($arr, COUNT_RECURSIVE). Для обхода всех листьев используйтеarray_walk_recursive()вместоarray_walk(). - Семантика копирования. Массивы PHP копируются по значению, поэтому
$copy = $original;с последующим изменением$copy[0][0]не меняет$original— в отличие от ссылок на объекты.
Заключение
Многомерные массивы позволяют представлять таблицы, сетки и сгруппированные записи, вкладывая один массив в другой. Читайте и записывайте отдельные ячейки через цепочку индексов [], выполняйте итерацию с помощью вложенных циклов foreach, а также преобразовывайте данные с помощью array_map(), array_column() и рекурсивных функций массивов. Для более широкого обзора инструментария массивов PHP смотрите PHP Arrays.