array_map в PHP: применение и нюансы
Основное применение array_map
Наиболее эффективный способ применить функцию к каждому элементу массива - использовать встроенную функцию array_map. Она принимает callback и один или несколько массивов, возвращает массив результатов. Базовый синтаксис:
$numbers = [1, 2, 3, 4];
$squared = array_map(function($n) {
return $n * $n;
}, $numbers);
print_r($squared);
Array
(
[0] => 1
[1] => 4
[2] => 9
[3] => 16
)
Callback может быть анонимной функцией, именованной функцией или статическим методом. Основное преимущество - лаконичность и читаемость по сравнению с циклом foreach.
Как применить существующую функцию к каждому элементу массива?
Если функция уже объявлена, можно передать её имя строкой:
$strings = [' hello ', ' world '];
$trimmed = array_map('trim', $strings);
// ['hello', 'world']
Такой подход удобен для встроенных функций PHP (strtolower, abs и т.д.).
Как быстро получить массив ключей ассоциативного массива?
Передача null в качестве callback заставляет array_map объединить массивы в один:
$array = ['a' => 1, 'b' => 2, 'c' => 3];
$keys = array_map(null, array_keys($array));
// Такой подход менее эффективен, проще использовать array_keys() напрямую.
// Но показан как иллюстрация.
На практике для получения ключей лучше применять array_keys.
Как передать дополнительный параметр в callback?
Можно воспользоваться замыканием use:
$multiplier = 10;
$numbers = [1, 2, 3];
$result = array_map(function($n) use ($multiplier) {
return $n * $multiplier;
}, $numbers);
// [10, 20, 30]
Типичные ошибки и их решение
- Передача не callable значения. Если передать строку, не являющуюся именем функции, или массив, не являющийся допустимым callback, PHP выдаст фатальную ошибку. Перед передачей следует проверять существование функции через function_exists.
- Исходные ключи не сохраняются. array_map возвращает массив с числовыми индексами, начиная с 0, даже если исходный массив был ассоциативным. Для сохранения ключей используют array_walk или array_combine с array_map.
- Игнорирование несоответствия длин массивов при передаче нескольких массивов. Если массивы разной длины, результат будет дополнен значениями null для недостающих элементов. Это часто приводит к неожиданному поведению, если не контролировать длины.
Использование array_map с несколькими массивами
Одна из сильных сторон array_map - возможность параллельной обработки нескольких массивов. Callback получает столько аргументов, сколько массивов передано. Пример - поэлементное сложение:
$a = [1, 2, 3];
$b = [10, 20, 30];
$sum = array_map(function($x, $y) {
return $x + $y;
}, $a, $b);
// [11, 22, 33]
Это проще и быстрее, чем писать цикл с индексами.
Как объединить три массива в один многомерный?
Передача null в качестве callback для нескольких массивов создаёт массив массивов из элементов с одинаковыми индексами:
$names = ['Alice', 'Bob'];
$ages = [25, 30];
$cities = ['NY', 'LA'];
$combined = array_map(null, $names, $ages, $cities);
print_r($combined);
Array
(
[0] => Array
(
[0] => Alice
[1] => 25
[2] => NY
)
[1] => Array
(
[0] => Bob
[1] => 30
[2] => LA
)
)
Этот приём заменяет ручное создание массива через цикл.
Как создать ассоциативный массив из двух массивов?
Если нужно получить [имя => возраст], используют array_combine, но с array_map тоже можно:
$keys = ['Alice', 'Bob'];
$values = [25, 30];
$assoc = array_combine($keys, $values);
// Но array_map не подходит, так как он не сохраняет ключи.
В данном случае array_combine - более прямое решение.
Проблема: разная длина массивов
Если массивы имеют разную длину, array_map дополнит короткие массивы значениями null до длины самого длинного. Это может вызвать ошибки внутри callback, если не обрабатывать null явно. Рекомендуется синхронизировать длины массивов перед вызовом.
Расширенные возможности и альтернативы
Как применить метод объекта к каждому элементу?
Можно передать массив [объект, 'methodName']:
class Greeter {
public function greet($name) {
return "Hello, $name";
}
}
$greeter = new Greeter();
$names = ['Alice', 'Bob'];
$greetings = array_map([$greeter, 'greet'], $names);
// ['Hello, Alice', 'Hello, Bob']
Также можно передать статический метод как ['ClassName', 'method'].
Как использовать array_map для преобразования строк в многомерном массиве?
Для вложенных массивов одного уровня array_map не рекурсивен. Приходится применять рекурсивную функцию-обёртку. Пример:
$matrix = [[1,2],[3,4]];
// Не сработает:
// array_map('sqrt', $matrix);
// Решение:
function recursive_map($callback, $array) {
return array_map(function($element) use ($callback) {
return is_array($element) ? recursive_map($callback, $element) : $callback($element);
}, $array);
}
$result = recursive_map('sqrt', $matrix);
Чем array_map отличается от array_walk?
array_walk работает с массивом по ссылке, сохраняет ключи и позволяет передавать ключ как второй аргумент в callback. Если нужно изменить элементы на месте - выбирают array_walk. Если нужно вернуть новый массив - array_map.
Ошибка: попытка сохранить ключи через array_map
Многие разработчики ожидают, что array_map сохранит ассоциативные ключи. Этого не происходит. Решение - использовать array_walk или сформировать массив вручную через array_combine с сохранением ключей.
Расширенные примеры использования array_map
Пример 1. Фильтрация и преобразование в одном проходе
Комбинируем array_map с array_filter для сложной обработки. Сначала фильтруем, потом маппим, или наоборот. Ниже - получение квадратов только чётных чисел:
$numbers = [1, 2, 3, 4, 5, 6];
$evenSquares = array_map(
fn($n) => $n * $n,
array_filter($numbers, fn($n) => $n % 2 === 0)
);
print_r($evenSquares);
Array
(
[0] => 4
[1] => 16
[2] => 36
)
Обратите внимание: индексы сброшены из-за array_filter, затем array_map создаёт новые числовые индексы.
Пример 2. Преобразование строк в нижний регистр с несколькими массивами
$firstNames = ['ALICE', 'BOB'];
$lastNames = ['SMITH', 'JONES'];
$fullNames = array_map(
function($f, $l) { return strtolower("$f $l"); },
$firstNames, $lastNames
);
print_r($fullNames);
Array
(
[0] => alice smith
[1] => bob jones
)
Пример 3. Использование array_map для создания списка целых чисел из строк с десятичными дробями
$prices = ['10.99', '25.50', '3.75'];
$priceCents = array_map(
fn($p) => (int) round(floatval($p) * 100),
$prices
);
print_r($priceCents);
Array
(
[0] => 1099
[1] => 2550
[2] => 375
)
Пример 4. Применение статического метода класса
class MathHelper {
public static function double($x) {
return $x * 2;
}
}
$values = [1, 2, 3];
$doubled = array_map(['MathHelper', 'double'], $values);
print_r($doubled);
Array
(
[0] => 2
[1] => 4
[2] => 6
)
Пример 5. Маппинг с сохранением ключей через array_combine
Применяем array_map к значениям, а ключи оставляем исходными:
$original = ['a' => 1, 'b' => 2, 'c' => 3];
$keys = array_keys($original);
$values = array_map(fn($v) => $v * 10, $original);
$result = array_combine($keys, $values);
print_r($result);
Array
(
[a] => 10
[b] => 20
[c] => 30
)
Пример 6. Рекурсивный array_map для произвольно вложенных массивов
Случай, когда нужно применить callback ко всем листовым элементам многомерного массива:
function array_map_recursive(callable $callback, array $array): array {
$result = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$result[$key] = array_map_recursive($callback, $value);
} else {
$result[$key] = $callback($value);
}
}
return $result;
}
$nested = [
'first' => ['a' => 1, 'b' => 2],
'second' => 3
];
$transformed = array_map_recursive(fn($x) => $x * 3, $nested);
print_r($transformed);
Array
(
[first] => Array
(
[a] => 3
[b] => 6
)
[second] => 9
)
Этот подход сохраняет ассоциативные ключи на всех уровнях, в отличие от стандартного array_map.
Пример 7. Использование array_map с генератором итераций (Range)
$range = range(1, 5);
$factorials = array_map(function($n) {
return array_product(range(1, $n));
}, $range);
print_r($factorials);
Array
(
[0] => 1
[1] => 2
[2] => 6
[3] => 24
[4] => 120
)
Пример 8. Маппинг с использованием стрелочных функций (PHP 7.4+)
$celsius = [0, 10, 20, 30];
$fahrenheit = array_map(fn($c) => $c * 9/5 + 32, $celsius);
print_r($fahrenheit);
Array
(
[0] => 32
[1] => 50
[2] => 68
[3] => 86
)
Пример 9. Обработка null-значений при разных длинах массивов
$a = [1, 2];
$b = [10, 20, 30];
$result = array_map(function($x, $y) {
return ($x ?? 0) + ($y ?? 0);
}, $a, $b);
print_r($result);
Array
(
[0] => 11
[1] => 22
[2] => 30
)
Здесь третий элемент получен из null (в $a) + 30.