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

285

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

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

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

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

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

Требуемый вывод:

$my_array = array("foo" => 1);
  • 0
    Не решение, а другой подход, который может быть полезен, состоит в том, чтобы $b = ['foo' => $a['foo'], 'bar' => $a['bar']] Это приведет к $b['bar'] будет null .
Теги:
arrays

12 ответов

201
Лучший ответ

В 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
);

Очевидно, что это не так элегантно, как 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,
    function ($val, $key) use ($allowed) { // N.b. $val, $key not $key, $val
        return isset($allowed[$key]) && (
            $allowed[$key] === true || $allowed[$key] === $val
        );
    },
    ARRAY_FILTER_USE_BOTH
); // ['foo' => 1, 'bar' => 'baz']
  • 16
    Блин, как автору этой функции я должен был искать этот вопрос ;-)
  • 1
    Спасибо, это лучше, чем array_intersect
382

С array_intersect_key и array_flip:

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

array(1) {
  ["foo"]=>
  int(1)
}
  • 1
    Мне любопытно, если это более эффективно, чем мое решение, хотя? Это определенно более элегантно :)
  • 12
    Это не общее решение, потому что оно потребует уникальности каждого значения. Изменить: извините .. Я неправильно понял решение. Нажатие на разрешенные клавиши является хорошим решением (+1)
Показать ещё 9 комментариев
39

Мне нужно было сделать то же самое, но с более сложным 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
)
8

Вот более гибкое решение, использующее закрытие:

$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)
}

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

  • 0
    Я бы точно не назвал это «более гибким»; оно кажется гораздо менее простым, чем принятое решение.
  • 0
    Согласен. Это было бы более гибким, если бы условие было более сложным.
Показать ещё 2 комментария
4

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

Независимо от того, как мне нравится решение Vincent для проблемы Maček, оно фактически не использует 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 не традиционно перебирает массив, увеличивая его внутренний внутренний указатель, вы должны заранее его продвигать.

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

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

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

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

$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
)
  • 0
    спасибо за Ваш ответ. Я хотел бы представить вам, что использование stristr в «работе» функции делает некоторые предположения для конечного пользователя. Возможно, было бы лучше позволить пользователю передавать регулярное выражение; это дало бы им больше гибкости в отношении определенных вещей, таких как привязки, границы слов, чувствительность к регистру и т. д.
  • 0
    Я добавил адаптацию вашего ответа, которая может помочь другим людям
Показать ещё 1 комментарий
3

Начиная с 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;
    }
}
3

Здесь менее гибкая альтернатива, использующая unset():

$array = array(
    1 => 'one',
    2 => 'two',
    3 => 'three'
);
$disallowed = array(1,3);
foreach($disallowed as $key){
    unset($array[$key]);
}

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

Array
(
    [2] => two
)

Это неприменимо, если вы хотите сохранить отфильтрованные значения для последующего использования, но более аккуратные, если вы уверены, что этого не делаете.

  • 1
    Вы должны проверить, существует ли ключ $ key в массиве $ перед выполнением unset.
  • 3
    @JarekJakubowski вам не нужно проверять, существует ли ключ массива при использовании unset() . Предупреждения не выдаются, если ключ не существует.
1

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

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

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

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

0

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

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 ($ выход);
Изображение 765

0

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

array_filter ( $array, $callback_function, $flag )

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

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

$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 ) 
0

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

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];

}
  • 2
    Это самый уродливый код, который я когда-либо видел.
Сообщество Overcoder
Наверх
Меню