Avatar ·

Замена ключей массива на основе другого массива соответствий (lookup/mapping)

Привет, коллеги! Я новичок в PHP и столкнулся с задачей, которая кажется простой, но я не могу найти элегантное решение. У меня есть исходный ассоциативный массив с данными о пользователях, например: ```php $userData = [ 'user_id' => 42, 'first_name' => 'Иван', 'last_name' => 'Петров', 'email_address' => 'ivan@example.com', 'phone_number' => '+7-999-123-45-67', 'registration_date' => '2023-05-15' ]; ``` И есть второй массив, который определяет, как нужно переименовать ключи (отображение старых ключей на новые): ```php $keyMapping = [ 'user_id' => 'id', 'first_name' => 'name', 'last_name' => 'surname', 'email_address' => 'email', 'phone_number' => 'phone', 'registration_date' => 'registered_at' ]; ``` Мне нужно получить новый массив, где ключи заменены в соответствии с $keyMapping. То есть на выходе должно быть: ```php $result = [ 'id' => 42, 'name' => 'Иван', 'surname' => 'Петров', 'email' => 'ivan@example.com', 'phone' => '+7-999-123-45-67', 'registered_at' => '2023-05-15' ]; ``` Я пробовал использовать array_flip и array_merge, но запутался. Пытался написать цикл foreach, но получается громоздко. Подскажите, как это сделать правильно и компактно? Важно, чтобы если в исходном массиве есть ключи, которых нет в $keyMapping, они просто оставались без изменений. Заранее спасибо! <<>>

Решение с помощью array_combine и array_intersect_key

Ваша задача — классический случай замены ключей ассоциативного массива с помощью lookup-массива. Есть несколько способов, но я покажу самый элегантный и производительный.

Основная идея: мы берём значения исходного массива, заменяем их ключи на новые из маппинга, а затем объединяем с теми элементами, которые не попали в маппинг (если такие есть).

function replaceKeys(array $array, array $keyMapping): array {
    // 1. Выбираем только те ключи, которые есть в маппинге
    $mappedValues = array_intersect_key($array, $keyMapping);
    
    // 2. Заменяем ключи на новые
    $mapped = array_combine(
        array_map(fn($key) => $keyMapping[$key], array_keys($mappedValues)),
        array_values($mappedValues)
    );
    
    // 3. Добавляем элементы, которых нет в маппинге (они остаются без изменений)
    $unmapped = array_diff_key($array, $keyMapping);
    
    // 4. Объединяем и возвращаем
    return $mapped + $unmapped;
}

// Использование:
$userData = [
    'user_id' => 42,
    'first_name' => 'Иван',
    'last_name' => 'Петров',
    'email_address' => 'ivan@example.com',
    'phone_number' => '+7-999-123-45-67',
    'registration_date' => '2023-05-15',
    'status' => 'active' // этот ключ не в маппинге
];

$keyMapping = [
    'user_id' => 'id',
    'first_name' => 'name',
    'last_name' => 'surname',
    'email_address' => 'email',
    'phone_number' => 'phone',
    'registration_date' => 'registered_at'
];

$result = replaceKeys($userData, $keyMapping);
print_r($result);

Как это работает пошагово:

  • array_intersect_key — оставляет только те элементы исходного массива, ключи которых присутствуют в $keyMapping. Это гарантирует, что мы не пытаемся заменить то, чего нет.
  • array_map с fn() — для каждого старого ключа получаем новый из маппинга. Используем стрелочную функцию (PHP 7.4+).
  • array_combine — создаёт новый ассоциативный массив из массива новых ключей и массива значений.
  • array_diff_key — находит элементы, ключи которых отсутствуют в маппинге. Они останутся как есть.
  • Оператор + — объединяет два массива. Важно: + не перезаписывает элементы, если ключ уже существует (в отличие от array_merge).

Альтернатива с циклом (для PHP 7.3 и ниже):

function replaceKeysLoop(array $array, array $keyMapping): array {
    $result = [];
    foreach ($array as $key => $value) {
        $newKey = $keyMapping[$key] ?? $key;
        $result[$newKey] = $value;
    }
    return $result;
}

Этот вариант проще для понимания, но чуть медленнее на больших массивах. Оператор ?? (null coalescing) проверяет, есть ли ключ в маппинге, и если нет — оставляет старый.

Важные замечания:

  • Дублирование ключей: если в маппинге два старых ключа ведут к одному новому — последний перезапишет первый. Цикл в этом плане даёт больше контроля.
  • Порядок ключей: первый метод сохраняет порядок: сначала идут заменённые элементы (в порядке исходного массива), потом — оставшиеся. Цикл сохраняет исходный порядок полностью.
  • Производительность: на массивах до 10 000 элементов разница незаметна. Для больших данных лучше использовать первый метод с функциями array_*.

Выбирайте тот вариант, который понятнее вам и вашей команде. Оба корректно решают задачу.

<<>>

Решение через array_walk (модификация исходного массива)

Если вы не против изменить исходный массив напрямую (или готовы сделать копию), можно использовать array_walk с замыканием. Этот подход особенно удобен, когда нужно выполнить дополнительные действия при замене.

$result = $userData; // копируем, чтобы не портить оригинал
array_walk($result, function(&$value, $key) use ($keyMapping, &$result) {
    if (isset($keyMapping[$key])) {
        $newKey = $keyMapping[$key];
        $result[$newKey] = $value;
        unset($result[$key]); // удаляем старый ключ
    }
});

Плюсы:

  • Работает на любой версии PHP (начиная с 4).
  • Легко добавить логирование или проверки (например, предупреждение, если старый ключ не найден).
  • Не требует создания промежуточных массивов.

Минусы:

  • Меняет массив «на лету», что может привести к неожиданностям (например, если в маппинге есть циклические ссылки).
  • Чуть медленнее на больших объёмах данных из-за операций unset внутри цикла.

Этот вариант хорошо подходит, когда маппинг гарантированно не содержит дубликатов новых ключей и вы уверены, что исходный массив не нужен в первозданном виде.