PHP: как использовать array_filter () для фильтрации ключей массива?

Функция обратного вызова в array_filter() передает только значения массива, а не ключи .

Если у меня есть:

$my_array = array("foo" => 1, "hello" => "world");

$allowed = array("foo", "bar");

Как лучше всего удалить все ключи в $my_array, которых нет в массиве $allowed?

Желаемый результат:

$my_array = array("foo" => 1);

person maček    schedule 23.11.2010    source источник
comment
Не решение, а другой подход, который может быть полезен: $b = ['foo' => $a['foo'], 'bar' => $a['bar']] Это приведет к $b['bar'] будет null.   -  person oriadam    schedule 02.04.2018


Ответы (15)


PHP 5.6 представил третий параметр для array_filter(), flag, который можно установить на ARRAY_FILTER_USE_KEY для фильтрации по ключу, а не по значению:

$my_array = ['foo' => 1, 'hello' => 'world'];
$allowed  = ['foo', 'bar'];
$filtered = array_filter(
    $my_array,
    function ($key) use ($allowed) {
        return in_array($key, $allowed);
    },
    ARRAY_FILTER_USE_KEY
);

Поскольку в PHP 7.4 появились стрелочные функции, мы можем сделать это более лаконичным:

$my_array = ['foo' => 1, 'hello' => 'world'];
$allowed  = ['foo', 'bar'];
$filtered = array_filter(
    $my_array,
    fn ($key) => in_array($key, $allowed),
    ARRAY_FILTER_USE_KEY
);

Ясно, что это не так элегантно, как array_intersect_key($my_array, array_flip($allowed)), но оно предлагает дополнительную гибкость выполнения произвольного теста по ключу, например $allowed может содержать шаблоны регулярных выражений вместо простых строк.

Вы также можете использовать ARRAY_FILTER_USE_BOTH, чтобы иметь и значение, и ключ, переданные вашей функции фильтрации. Вот надуманный пример, основанный на первом, но обратите внимание, что я бы не рекомендовал кодировать правила фильтрации с использованием $allowed таким образом:

$my_array = ['foo' => 1, 'bar' => 'baz', 'hello' => 'wld'];
$allowed  = ['foo' => true, 'bar' => true, 'hello' => 'world'];
$filtered = array_filter(
    $my_array,
    // N.b. it's ($val, $key) not ($key, $val):
    fn ($val, $key) => isset($allowed[$key]) && (
        $allowed[$key] === true || $allowed[$key] === $val
    ),
    ARRAY_FILTER_USE_BOTH
); // ['foo' => 1, 'bar' => 'baz']
person Richard Turner    schedule 23.10.2014
comment
Черт, как автор этой функции, мне следовало искать этот вопрос; -) - person Ja͢ck; 08.06.2015
comment
Спасибо, это лучше, чем array_intersect - person brzuchal; 04.02.2016
comment
PHP 7.4+ $filtered = array_filter( $my_array, fn ($key) => in_array($key, $allowed), ARRAY_FILTER_USE_KEY ); - person jartaud; 27.02.2021

С помощью array_intersect_key и array_flip :

var_dump(array_intersect_key($my_array, array_flip($allowed)));

array(1) {
  ["foo"]=>
  int(1)
}
person Vincent Savard    schedule 23.11.2010
comment
Мне любопытно, хотя это более эффективно, чем мое решение? Это определенно более элегантно :) - person GWW; 23.11.2010
comment
@GWW, Как правило, я обнаружил, что эти типы функций массива быстрее, чем эквивалентный цикл foreach (а иногда и значительно), но единственный способ узнать наверняка - это синхронизировать их оба с одними и теми же данными. - person Matthew; 23.11.2010
comment
Зачем использовать array_flip? Просто определите $allowed с помощью ключей: allowed = array ( 'foo' => 1, 'bar' => 1 ); - person Yuval A.; 04.06.2017

Мне нужно было сделать то же самое, но с более сложным array_filter на клавишах.

Вот как я это сделал, используя похожий метод.

// Filter out array elements with keys shorter than 4 characters
$a = array(
  0      => "val 0", 
  "one"  => "val one", 
  "two"  => "val two", 
  "three"=> "val three", 
  "four" => "val four", 
  "five" => "val five", 
  "6"    => "val 6"
); 

$f = array_filter(array_keys($a), function ($k){ return strlen($k)>=4; }); 
$b = array_intersect_key($a, array_flip($f));
print_r($b);

Это выводит результат:

Array
(
    [three] => val three
    [four] => val four
    [five] => val five
)
person Christopher    schedule 27.08.2012

Вот более гибкое решение с использованием закрытия:

$my_array = array("foo" => 1, "hello" => "world");
$allowed = array("foo", "bar");
$result = array_flip(array_filter(array_flip($my_array), function ($key) use ($allowed)
{
    return in_array($key, $allowed);
}));
var_dump($result);

Выходы:

array(1) {
  'foo' =>
  int(1)
}

Таким образом, в функции вы можете выполнять другие специальные тесты.

person COil    schedule 25.01.2013
comment
Я бы не назвал это более гибким; это тоже кажется намного менее простым, чем принятое решение. - person maček; 27.01.2013
comment
Я согласен. Было бы более гибко, если бы условие было более сложным. - person COil; 31.01.2013
comment
Просто прохождение для других пользователей: это решение не касается случая, когда $ my_array имеет повторяющиеся значения или значения, которые не являются целыми числами или строками. Поэтому я бы не стал использовать это решение. - person user23127; 09.06.2014
comment
Я согласен, что это более гибкий вариант, поскольку он позволяет изменять логику фильтра. Например, я использовал массив запрещенных ключей и просто вернул! In_array ($ key, $ disallowed). - person nfplee; 07.09.2014

Если вы ищете метод фильтрации массива по строке, содержащейся в ключах, вы можете использовать:

$mArray=array('foo'=>'bar','foo2'=>'bar2','fooToo'=>'bar3','baz'=>'nope');
$mSearch='foo';
$allowed=array_filter(
    array_keys($mArray),
    function($key) use ($mSearch){
        return stristr($key,$mSearch);
    });
$mResult=array_intersect_key($mArray,array_flip($allowed));

Результат print_r($mResult)

Array ( [foo] => bar [foo2] => bar2 [fooToo] => bar3 )

Адаптация этого ответа, поддерживающая регулярные выражения

function array_preg_filter_keys($arr, $regexp) {
  $keys = array_keys($arr);
  $match = array_filter($keys, function($k) use($regexp) {
    return preg_match($regexp, $k) === 1;
  });
  return array_intersect_key($arr, array_flip($match));
}

$mArray = array('foo'=>'yes', 'foo2'=>'yes', 'FooToo'=>'yes', 'baz'=>'nope');

print_r(array_preg_filter_keys($mArray, "/^foo/i"));

Выход

Array
(
    [foo] => yes
    [foo2] => yes
    [FooToo] => yes
)
person Nicolas Zimmer    schedule 28.02.2014
comment
спасибо за Ваш ответ. Я хотел бы сообщить вам, что использование stristr в работе функции делает некоторые предположения для конечного пользователя. Возможно, было бы лучше разрешить пользователю передавать регулярное выражение; это дало бы им больше гибкости в отношении определенных вещей, таких как привязки, границы слов, чувствительность к регистру и т. д. - person maček; 02.03.2014
comment
Я добавил адаптацию вашего ответа, которая может помочь другим людям - person maček; 03.03.2014
comment
Вы, конечно, правы, maček, это более универсальный подход для пользователей, которые привыкли к регулярным выражениям. Спасибо. - person Nicolas Zimmer; 03.03.2014

Как получить текущий ключ массива при использовании array_filter

Независимо от того, насколько мне нравится решение Винсента для проблемы Мачека, на самом деле оно не использует array_filter. Если вы пришли сюда из поисковой системы, возможно, вы где-то искали что-то вроде этого (PHP> = 5.3):

$array = ['apple' => 'red', 'pear' => 'green'];
reset($array); // Unimportant here, but make sure your array is reset

$apples = array_filter($array, function($color) use ($&array) {
  $key = key($array);
  next($array); // advance array pointer

  return key($array) === 'apple';
}

Он передает массив, который вы фильтруете, как ссылку на обратный вызов. Поскольку array_filter обычно не выполняет итерацию по массиву, увеличивая его общедоступный внутренний указатель, вы должны продвигать его самостоятельно.

Здесь важно то, что вам нужно убедиться, что ваш массив сброшен, иначе вы можете начать прямо с его середины.

В PHP> = 5.4 вы можете сделать обратный вызов еще короче:

$apples = array_filter($array, function($color) use ($&array) {
  return each($array)['key'] === 'apple';
}
person flu    schedule 23.01.2015

Начиная с PHP 5.6, вы можете использовать флаг ARRAY_FILTER_USE_KEY в array_filter:

$result = array_filter($my_array, function ($k) use ($allowed) {
    return in_array($k, $allowed);
}, ARRAY_FILTER_USE_KEY);


В противном случае вы можете использовать эту функцию (из TestDummy ):

function filter_array_keys(array $array, $callback)
{
    $matchedKeys = array_filter(array_keys($array), $callback);

    return array_intersect_key($array, array_flip($matchedKeys));
}

$result = filter_array_keys($my_array, function ($k) use ($allowed) {
    return in_array($k, $allowed);
});


А вот и моя расширенная версия, которая принимает обратный вызов или напрямую ключи:

function filter_array_keys(array $array, $keys)
{
    if (is_callable($keys)) {
        $keys = array_filter(array_keys($array), $keys);
    }

    return array_intersect_key($array, array_flip($keys));
}

// using a callback, like array_filter:
$result = filter_array_keys($my_array, function ($k) use ($allowed) {
    return in_array($k, $allowed);
});

// or, if you already have the keys:
$result = filter_array_keys($my_array, $allowed));


И последнее, но не менее важное: вы также можете использовать простой foreach:

$result = [];
foreach ($my_array as $key => $value) {
    if (in_array($key, $allowed)) {
        $result[$key] = $value;
    }
}
person Gras Double    schedule 29.08.2015


функция фильтра массива из php:

array_filter ( $array, $callback_function, $flag )

$ array - это входной массив

$ callback_function - функция обратного вызова для использования , Если функция обратного вызова возвращает true, текущее значение из массива возвращается в массив результатов.

$ flag - это необязательный параметр, он определяет, какие аргументы отправляются в функцию обратного вызова. Если этот параметр пуст, функция обратного вызова примет значения массива в качестве аргумента. Если вы хотите отправить ключ массива в качестве аргумента, используйте $ flag как ARRAY_FILTER_USE_KEY. Если вы хотите отправлять и ключи, и значения, вы должны использовать флаг $ как ARRAY_FILTER_USE_BOTH.

Например: рассмотрим простой массив

$array = array("a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5);

Если вы хотите фильтровать массив на основе ключа массива, нам нужно использовать ARRAY_FILTER_USE_KEY в качестве третьего параметра функции массива array_filter.

$get_key_res = array_filter($array,"get_key",ARRAY_FILTER_USE_KEY );

Если вы хотите фильтровать массив на основе ключа массива и значения массива, нам нужно использовать ARRAY_FILTER_USE_BOTH в качестве третьего параметра функции массива array_filter.

$get_both = array_filter($array,"get_both",ARRAY_FILTER_USE_BOTH );

Примеры функций обратного вызова:

 function get_key($key)
 {
    if($key == 'a')
    {
        return true;
    } else {
        return false;
    }
}
function get_both($val,$key)
{
    if($key == 'a' && $val == 1)
    {
        return true;
    }   else {
        return false;
    }
}

Он выведет

Output of $get_key is :Array ( [a] => 1 ) 
Output of $get_both is :Array ( [a] => 1 ) 
person prince jose    schedule 11.01.2017

На основе @sepiariver я провел аналогичное тестирование на PHP 8.0.3:

$arr = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8];
$filter = ['a', 'e', 'h'];


$filtered = [];
$time = microtime(true);
$i = 1000000;
while($i) {
  $filtered = array_intersect_key($arr, array_flip($filter));
  $i--;
}
print_r($filtered);
echo microtime(true) - $time . " using array_intersect_key\n\n";


$filtered = [];
$time = microtime(true);
$i = 1000000;
while($i) {
  $filtered = array_filter(
    $arr,
    function ($key) use ($filter){return in_array($key, $filter);},
    ARRAY_FILTER_USE_KEY
  );
  $i--;
}
print_r($filtered);
echo microtime(true) - $time . " using array_filter\n\n";

$filtered = [];
$time = microtime(true);
$i = 1000000;
while($i) {
  foreach ($filter as $key)
    if(array_key_exists($key, $arr))
      $filtered[$key] = $arr[$key];
  $i--;
}
print_r($filtered);
echo microtime(true) - $time . " using foreach + array_key_exists\n\n";
  • 0.28603601455688 с использованием array_intersect_key
  • 1.3096671104431 с использованием array_filter
  • 0.19402384757996 с использованием foreach + array_key_exists

«Проблема» array_filter в том, что он будет перебирать все элементы $ arr, в то время как array_filter и foreach перебирают только $ filter. Последний более эффективен, если $ filter меньше $ arr.

person CrAzY_pRoGrAmMeR    schedule 11.06.2021

Возможно, это будет излишним, если вам понадобится всего один раз, но вы можете использовать библиотеку YaLinqo * для фильтрации коллекций (и выполнения любые другие преобразования). Эта библиотека позволяет выполнять SQL-подобные запросы к объектам с плавным синтаксисом. Его функция where принимает обратный вызов с двумя аргументами: значение и ключ. Например:

$filtered = from($array)
    ->where(function ($v, $k) use ($allowed) {
        return in_array($k, $allowed);
    })
    ->toArray();

(Функция where возвращает итератор, поэтому, если вам нужно выполнить итерацию с foreach по результирующей последовательности только один раз, ->toArray() можно удалить.)

* разработан мной

person Athari    schedule 04.06.2015

С помощью этой функции вы можете фильтровать многомерный массив

function filter_array_keys($array,$filter_keys=array()){

    $l=array(&$array);
    $c=1;
    //This first loop will loop until the count var is stable//
    for($r=0;$r<$c;$r++){
        //This loop will loop thru the child element list//
        $keys = array_keys($l[$r]);

        for($z=0;$z<count($l[$r]);$z++){
            $object = &$l[$r][$keys[$z]];

            if(is_array($object)){
                $i=0;
                $keys_on_array=array_keys($object);
                $object=array_filter($object,function($el) use(&$i,$keys_on_array,$filter_keys){
                    $key = $keys_on_array[$i];
                    $i++;

                    if(in_array($key,$filter_keys) || is_int($key))return false;                
                    return true;                        
                });
            }

            if(is_array($l[$r][$keys[$z]])){
                $l[] = &$l[$r][$keys[$z]];
                $c++;
            }//IF           
        }//FOR
    }//FOR  

    return $l[0];

}
person user1220713    schedule 15.03.2016

Наивное и некрасивое (но, кажется, более быстрое) решение?

Пробовал это только в php 7.3.11, но уродливый цикл, похоже, выполняется примерно в трети случаев. Аналогичные результаты для массива с несколькими сотнями ключей. Микрооптимизация, вероятно, бесполезна в RW, но показалась удивительной и интересной:

$time = microtime(true);
$i = 100000;
while($i) {
    $my_array = ['foo' => 1, 'hello' => 'world'];
    $allowed  = ['foo', 'bar'];
    $filtered = array_filter(
        $my_array,
        function ($key) use ($allowed) {
            return in_array($key, $allowed);
        },
        ARRAY_FILTER_USE_KEY
    );
    $i--;
}
print_r($filtered);
echo microtime(true) - $time . ' on array_filter';

// 0.40600109100342 on array_filter
$time2 = microtime(true);
$i2 = 100000;
while($i2) {
    $my_array2 = ['foo' => 1, 'hello' => 'world'];
    $allowed2  = ['foo', 'bar'];
    $filtered2 = [];
    foreach ($my_array2 as $k => $v) {
        if (in_array($k, $allowed2)) $filtered2[$k] = $v;
    }
    $i2--;
}
print_r($filtered2);
echo microtime(true) - $time2 . ' on ugly loop';
// 0.15677785873413 on ugly loop
person sepiariver    schedule 26.11.2019

// Отфильтровать элементы массива с ключами короче 4 символов // Используя анонимную функцию с Closure ...

function comparison($min)
{
   return function($item) use ($min) { 
      return strlen($item) >= $min;   
   }; 
}

$input = array(
  0      => "val 0",
  "one"  => "val one",
  "two"  => "val two",
  "three"=> "val three",
  "four" => "val four",  
  "five" => "val five",    
  "6"    => "val 6"    
);

$ output = array_filter (array_keys ($ input), сравнение (4));

print_r ($ output);
 введите описание изображения здесь

person ZOB    schedule 24.10.2017

$elements_array = ['first', 'second'];

функция для удаления некоторых элементов массива

function remove($arr, $data) {
    return array_filter($arr, function ($element) use ($data) {
        return $element != $data;
    });
}

позвони и распечатай

print_r(remove($elements_array, 'second'));

результат Array ( [0] => first )

person Abdallah Awwad Alkhwaldah    schedule 24.08.2018
comment
Вопрос касался фильтрации ключей массива, а не значений. - person poletaew; 30.10.2018