PHP: Como usar array_filter () para filtrar chaves de matriz?


363

A função de retorno de chamada array_filter()passa apenas os valores da matriz, não as chaves.

Se eu tiver:

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

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

Qual é a melhor maneira de excluir todas as chaves $my_arrayque não estão na $allowedmatriz?

Saída desejada:

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

Não uma solução, mas uma outra abordagem que pode ser útil é $b = ['foo' => $a['foo'], 'bar' => $a['bar']]Isto irá resultar em $b['bar']seja null.
Oriadam 2/04

Respostas:


322

O PHP 5.6 introduziu um terceiro parâmetro para array_filter(), flagque você pode definir ARRAY_FILTER_USE_KEYpara filtrar por chave em vez de valor:

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

Claramente, isso não é tão elegante quanto array_intersect_key($my_array, array_flip($allowed)), mas oferece a flexibilidade adicional de executar um teste arbitrário contra a chave; por exemplo, $allowedpode conter padrões regex em vez de strings simples.

Você também pode usar ARRAY_FILTER_USE_BOTHpara passar o valor e a chave para a função de filtro. Aqui está um exemplo artificial baseado no primeiro, mas observe que eu não recomendaria a codificação de regras de filtragem $alloweddessa maneira:

$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']

21
Porra, como o autor desse recurso, eu deveria ter procurado por esta pergunta ;-) #
295

11
Obrigado, isso é melhor do quearray_intersect
brzuchal

461

Com array_intersect_keye array_flip:

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

array(1) {
  ["foo"]=>
  int(1)
}

11
Estou curioso para saber se isso é mais eficiente do que minha solução? Definitivamente, é mais elegante :) #
284 GWW

13
Esta não é uma solução geral, porque exigiria que cada valor fosse único. Edit: desculpe .. Eu li mal a solução. Virar as teclas permitidas é uma boa solução (+1)
Matthew

@GWW: Não sei se é mais eficiente, TBH. @ Konforce: Não tenho certeza de entender o seu ponto. Não pode haver duas chaves idênticas em uma matriz, portanto, ele retornará apenas chaves em $ my_array presentes em $ allowed.
Vincent Savard

11
Ou simplesmente use ARRAY_FILTER_USE_KEY: P
Julien Palard

11
Por que usar array_flip? Basta definir as $allowedteclas com:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.

43

Eu precisava fazer o mesmo, mas com mais complexidade array_filternas teclas.

Aqui está como eu fiz isso, usando um método semelhante.

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

Isso gera o resultado:

Array
(
    [three] => val three
    [four] => val four
    [five] => val five
)

8

Aqui está uma solução mais flexível usando um fechamento:

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

Saídas:

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

Portanto, na função, você pode fazer outros testes específicos.


11
Eu não chamaria exatamente isso de "mais flexível"; parece muito menos direto do que a solução aceita também.
Maček

Concordo. Seria mais flexível se a condição fosse mais complexa.
bobina

11
Só de passagem, para outros usuários: Esta solução não lida com o caso de o $ my_array ter valores duplicados ou valores que não são números inteiros ou seqüências de caracteres. Então, eu não usaria esta solução.
precisa saber é o seguinte

2
Concordo que isso é mais flexível, pois permite alterar a lógica do filtro. Por exemplo, usei uma matriz de chaves não permitidas e simplesmente retornei! In_array ($ key, $ disallowed).
Nfplee

5

Se você estiver procurando por um método para filtrar uma matriz por uma sequência que ocorre nas chaves, você pode usar:

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

O resultado de print_r($mResult)é

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

Uma adaptação desta resposta que suporta expressões regulares

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

Resultado

Array
(
    [foo] => yes
    [foo2] => yes
    [FooToo] => yes
)

obrigado pela sua resposta. Eu diria a você que o uso stristrdentro do "trabalho" da função está fazendo algumas suposições para o usuário final. Talvez seja melhor permitir que o usuário passe uma expressão regular; isso lhes daria mais flexibilidade sobre certas coisas, como âncoras, limites de palavra, e maiúsculas e minúsculas, etc.
Macek

Eu adicionei uma adaptação da sua resposta que pode ajudar outras pessoas #
21 /

11
Você está certo, certamente, que é uma abordagem mais versátil para usuários que se sentem confortáveis ​​com o regex. Obrigado.
Nicolas Zimmer

5

Como obter a chave atual de uma matriz ao usar array_filter

Independentemente de como eu goste da solução de Vincent para o problema de Maček, ele não é realmente usado array_filter. Se você veio aqui de um mecanismo de pesquisa, talvez esteja procurando algo parecido com este ( 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';
}

Ele passa a matriz que você está filtrando como uma referência ao retorno de chamada. Como array_filternão itera convencionalmente sobre a matriz, aumentando o ponteiro público interno, você precisa avançar por si mesmo.

O importante aqui é que você precisa garantir que sua matriz seja redefinida, caso contrário, você poderá começar bem no meio dela.

No PHP> = 5.4, você poderia reduzir ainda mais o retorno de chamada:

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

3

Aqui está uma alternativa menos flexível usando unset () :

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

O resultado de print_r($array)ser:

Array
(
    [2] => two
)

Isso não é aplicável se você deseja manter os valores filtrados para uso posterior, mas mais organizado, se tiver certeza de que não.


11
Você deve verificar se a chave $ key existe na matriz $ antes de desabilitar.
Jarek Jakubowski

3
@JarekJakubowski, você não precisa verificar se existe uma chave de matriz ao usar unset(). Nenhum aviso será emitido se a chave não existir.
Christopher

3

A partir do PHP 5.6, você pode usar o ARRAY_FILTER_USE_KEYsinalizador em array_filter:

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


Caso contrário, você pode usar esta função ( do 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);
});


E aqui está uma versão aumentada da minha, que aceita um retorno de chamada ou diretamente as chaves:

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


Por último, mas não menos importante, você também pode usar um simples foreach:

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

1

Talvez seja um exagero, se você precisar apenas uma vez, mas pode usar a biblioteca do YaLinqo * para filtrar coleções (e executar outras transformações). Essa biblioteca permite realizar consultas semelhantes a SQL em objetos com sintaxe fluente. Sua wherefunção aceita um calback com dois argumentos: um valor e uma chave. Por exemplo:

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

(A wherefunção retorna um iterador, portanto, se você precisar iterar com foreacha sequência resultante uma vez, ->toArray()poderá ser removido.)

* desenvolvido por mim


1

função de filtro de matriz do php:

array_filter ( $array, $callback_function, $flag )

$ array - É o array de entrada

$ callback_function - A função de retorno de chamada a ser usada , Se a função de retorno de chamada retornar verdadeiro , o valor atual da matriz será retornado para a matriz de resultados.

$ flag - É um parâmetro opcional , determinará quais argumentos serão enviados para a função de retorno de chamada. Se esse parâmetro estiver vazio, a função de retorno de chamada assumirá os valores da matriz como argumento. Se você deseja enviar a chave da matriz como argumento, use $ flag como ARRAY_FILTER_USE_KEY . Se você deseja enviar chaves e valores, use $ flag como ARRAY_FILTER_USE_BOTH .

Por exemplo: considere uma matriz simples

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

Se você deseja filtrar a matriz com base na chave da matriz , precisamos usar ARRAY_FILTER_USE_KEY como terceiro parâmetro da função da matriz array_filter.

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

Se você deseja filtrar a matriz com base na chave e no valor da matriz , precisamos usar ARRAY_FILTER_USE_BOTH como terceiro parâmetro da função da matriz array_filter.

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

Funções de amostra de retorno de chamada:

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

Vai sair

Output of $get_key is :Array ( [a] => 1 ) 
Output of $get_both is :Array ( [a] => 1 ) 

0

Com esta função, você pode filtrar uma matriz multidimensional

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

}

0
// Filter out array elements with keys shorter than 4 characters 
// By using Anonymous function with  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), comparison(4));    

print_r($output);

Saída da execução


0

Solução ingênua e feia (mas parece ser mais rápida)?

Só tentei isso no php 7.3.11, mas um loop feio parece ser executado em cerca de um terço do tempo. Resultados semelhantes em uma matriz com algumas centenas de chaves. Micro-otimização, provavelmente não útil em RW, mas achou surpreendente e interessante:

$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

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

função para remover alguns elementos da matriz

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

ligar e imprimir

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

o resultado Array ( [0] => first )


A pergunta era sobre a filtragem de chaves de matriz e não de valores.
Poletaew 30/10/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.