array_map в PHP: применение и нюансы

Раздел: Программирование на PHP -> array_map и array_search

Основное применение 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.

Функция array_map в PHP - comments

En
Php mapping (php)