Avatar ·

Как отфильтровать ассоциативный массив по ключам, используя значения из индексированного массива?

Всем привет! Я новичок в PHP и столкнулся с проблемой при работе с массивами. У меня есть ассоциативный массив $users, где ключи — это ID пользователей, а значения — их имена. Также есть индексированный массив $allowed_ids, который содержит список ID, которые нужно оставить. Вот пример данных: ```php $users = [ 101 => 'Анна', 102 => 'Борис', 103 => 'Виктор', 104 => 'Галина', 105 => 'Дмитрий' ]; $allowed_ids = [102, 104, 105]; ``` Мне нужно получить новый массив, который будет содержать только те элементы из $users, ключи которых присутствуют в $allowed_ids. То есть на выходе я хочу получить: ```php [ 102 => 'Борис', 104 => 'Галина', 105 => 'Дмитрий' ] ``` Я пробовал использовать array_filter(), но не понимаю, как передать в callback-функцию ключи массива, а не значения. Пробовал array_intersect_key(), но он работает с массивами, где ключи совпадают, а у меня $allowed_ids — это простой список значений. Подскажите, пожалуйста, как правильно решить эту задачу? Желательно с использованием встроенных функций PHP, без циклов foreach, если это возможно. Заранее спасибо! <<>>

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

Ваша задача решается очень элегантно с помощью комбинации двух встроенных функций PHP: array_flip() и array_intersect_key(). Давайте разберём пошагово, как это работает.

Пошаговое объяснение

Проблема в том, что array_intersect_key() сравнивает ключи двух массивов, а ваш $allowed_ids — это индексированный массив со значениями (102, 104, 105), а не с ключами. Нам нужно превратить эти значения в ключи. Для этого идеально подходит array_flip().

<?php
$users = [
    101 => 'Анна',
    102 => 'Борис',
    103 => 'Виктор',
    104 => 'Галина',
    105 => 'Дмитрий'
];

$allowed_ids = [102, 104, 105];

// Шаг 1: Меняем местами ключи и значения в $allowed_ids
$allowed_keys = array_flip($allowed_ids);
// Результат: [102 => 0, 104 => 1, 105 => 2]

// Шаг 2: Используем array_intersect_key для фильтрации
$filtered_users = array_intersect_key($users, $allowed_keys);

print_r($filtered_users);
?>

Результат выполнения:

Array
(
    [102] => Борис
    [104] => Галина
    [105] => Дмитрий
)

Как это работает

  • array_flip($allowed_ids) — создаёт новый массив, где значения исходного массива становятся ключами, а ключи — значениями. Так как исходный массив индексированный (ключи 0, 1, 2), после переворота мы получаем массив вида [102 => 0, 104 => 1, 105 => 2]. Теперь у нас есть массив с нужными ключами.
  • array_intersect_key($users, $allowed_keys) — возвращает массив, содержащий все элементы первого массива ($users), ключи которых присутствуют во втором массиве ($allowed_keys). Функция сравнивает только ключи, значения второго массива игнорируются. В результате остаются только элементы с ключами 102, 104 и 105.

Альтернатива с array_filter

Если вы предпочитаете использовать array_filter(), это тоже возможно, но нужно передать callback, который проверяет ключи. Для этого используется третий параметр ARRAY_FILTER_USE_KEY (доступен с PHP 5.6):

<?php
$filtered_users = array_filter(
    $users,
    function($key) use ($allowed_ids) {
        return in_array($key, $allowed_ids);
    },
    ARRAY_FILTER_USE_KEY
);
?>

Однако этот подход менее эффективен, так как in_array() выполняет линейный поиск по массиву на каждой итерации. Для больших массивов (10 000+ элементов) это может быть заметно медленнее, чем решение с array_flip() и array_intersect_key().

Советы по производительности

  • Решение с array_intersect_key() работает за O(n+m), где n — размер $users, m — размер $allowed_ids.
  • Решение с array_filter() + in_array() работает за O(n*m), что может быть проблемой на больших данных.
  • Если массив $allowed_ids очень большой, можно дополнительно использовать array_flip() и затем isset() для проверки в callback.

Надеюсь, это помогло! Если остались вопросы — спрашивайте.

<<>>

Решение с использованием array_filter и ARRAY_FILTER_USE_BOTH

Ещё один удобный способ — использовать array_filter() с флагом ARRAY_FILTER_USE_BOTH, который передаёт в callback и ключ, и значение. Это может быть полезно, если вам нужно проверять не только ключи, но и значения.

<?php
$users = [
    101 => 'Анна',
    102 => 'Борис',
    103 => 'Виктор',
    104 => 'Галина',
    105 => 'Дмитрий'
];

$allowed_ids = [102, 104, 105];

// Преобразуем $allowed_ids в массив для быстрой проверки (flip)
$allowed_map = array_flip($allowed_ids);

$filtered_users = array_filter(
    $users,
    function($value, $key) use ($allowed_map) {
        // Используем isset для быстрой проверки
        return isset($allowed_map[$key]);
    },
    ARRAY_FILTER_USE_BOTH
);
?>

Почему это может быть удобно

  • Callback получает и ключ, и значение — вы можете добавить дополнительные условия, например, фильтрацию по длине имени.
  • Использование isset() с перевёрнутым массивом даёт константную скорость проверки O(1), что быстрее in_array().
  • Код легко читается и модифицируется.

Однако для чистой фильтрации по ключам первый ответ (с array_intersect_key) остаётся самым лаконичным и производительным. Выбирайте подход под свою конкретную задачу!