PHP: Comment utiliser array_filter () pour filtrer les clés de tableau?

363

La fonction de rappel array_filter()ne transmet que les valeurs du tableau, pas les clés.

Si j'ai:

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

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

Quelle est la meilleure façon de supprimer toutes les clés $my_arrayqui ne sont pas dans le $allowedtableau?

Sortie désirée:

$my_array = array("foo" => 1);
maček
la source
Pas une solution , mais une autre approche qui pourrait être utile est de $b = ['foo' => $a['foo'], 'bar' => $a['bar']]Cela se traduira $b['bar']soit null.
oriadam

Réponses:

322

PHP 5.6 introduit un troisième paramètre à array_filter(), flagque vous pouvez définir pour ARRAY_FILTER_USE_KEYà filtrer par clé au lieu de la valeur:

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

De toute évidence, ce n'est pas aussi élégant que cela array_intersect_key($my_array, array_flip($allowed)), mais cela offre la flexibilité supplémentaire d'effectuer un test arbitraire contre la clé, par exemple, $allowedpourrait contenir des modèles d'expression régulière au lieu de chaînes simples.

Vous pouvez également utiliser ARRAY_FILTER_USE_BOTHpour que la valeur et la clé soient transmises à votre fonction de filtrage. Voici un exemple artificiel basé sur le premier, mais notez que je ne recommanderais pas de coder les règles de filtrage de $allowedcette manière:

$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']
Richard Turner
la source
21
Merde, en tant qu'auteur de cette fonctionnalité, j'aurais dû chercher cette question ;-)
Ja͢ck
1
Merci, c'est mieux quearray_intersect
brzuchal
461

Avec array_intersect_keyet array_flip:

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

array(1) {
  ["foo"]=>
  int(1)
}
Vincent Savard
la source
1
Je suis curieux de savoir si cela est plus efficace que ma solution? C'est certainement plus élégant :)
GWW
13
Ce n'est pas une solution générale car cela exigerait que chaque valeur soit unique. Edit: désolé .. J'ai mal lu la solution. Retourner les clés autorisées est une bonne solution (+1)
Matthew
@GWW: Je ne sais pas si c'est plus efficace, TBH. @konforce: Je ne suis pas sûr de comprendre votre point. Il ne peut pas y avoir deux clés identiques dans un tableau, donc il ne retournera que les clés dans $ my_array qui sont présentes dans $ allowed.
Vincent Savard
1
Ou utilisez simplement ARRAY_FILTER_USE_KEY: P
Julien Palard
1
Pourquoi utiliser array_flip? Définissez simplement les $allowedtouches avec:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.
43

J'avais besoin de faire de même, mais avec un plus complexe array_filtersur les touches.

Voici comment je l'ai fait, en utilisant une méthode similaire.

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

Cela produit le résultat:

Array
(
    [three] => val three
    [four] => val four
    [five] => val five
)
Christophe
la source
8

Voici une solution plus flexible utilisant une fermeture:

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

Les sorties:

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

Ainsi, dans la fonction, vous pouvez effectuer d'autres tests spécifiques.

Bobine
la source
1
Je n'appellerais pas exactement cela "plus flexible"; cela semble beaucoup moins simple que la solution acceptée.
maček
Je suis d'accord. Ce serait plus flexible si la condition était plus complexe.
COil
1
Juste en passant, pour les autres utilisateurs: Cette solution ne traite pas le cas où $ my_array a des valeurs en double ou des valeurs qui ne sont pas des entiers ou des chaînes. Je n'utiliserais donc pas cette solution.
user23127
2
Je suis d'accord que c'est plus flexible car cela vous permet de changer la logique du filtre. Par exemple, j'ai utilisé un tableau de clés non autorisées et j'ai simplement renvoyé! In_array ($ key, $ disallowed).
nfplee
5

Si vous recherchez une méthode pour filtrer un tableau par une chaîne apparaissant dans les clés, vous pouvez utiliser:

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

Le résultat de print_r($mResult)est

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

Une adaptation de cette réponse qui prend en charge les expressions régulières

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

Production

Array
(
    [foo] => yes
    [foo2] => yes
    [FooToo] => yes
)
Nicolas Zimmer
la source
Merci pour votre réponse. Je vous soumets que l'utilisation stristrdans le "travail" de la fonction fait des suppositions pour l'utilisateur final. Il serait peut-être préférable de permettre à l'utilisateur de passer une expression régulière; cela leur donnerait plus de flexibilité sur certaines choses comme les ancres, les limites de mots et la casse, etc.
maček
J'ai ajouté une adaptation de votre réponse qui pourrait aider d'autres personnes
maček
1
Vous avez certainement raison, maček, c'est une approche plus polyvalente pour les utilisateurs qui sont à l'aise avec l'expression régulière. Merci.
Nicolas Zimmer
5

Comment obtenir la clé actuelle d'un tableau lors de l'utilisation array_filter

Quelle que soit la façon dont j'aime la solution de Vincent pour le problème de Maček, elle ne l'utilise pas réellement array_filter. Si vous êtes venu ici d'un moteur de recherche, vous cherchez peut-être quelque chose comme ça ( 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';
}

Il transmet le tableau que vous filtrez comme référence au rappel. Comme il array_filtern'est pas conventionnel d'itérer sur le tableau en augmentant son pointeur interne public, vous devez le faire avancer vous-même.

Ce qui est important ici, c'est que vous devez vous assurer que votre baie est réinitialisée, sinon vous pourriez commencer en plein milieu.

En PHP> = 5.4, vous pourriez rendre le rappel encore plus court:

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

Voici une alternative moins flexible en utilisant unset () :

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

Le résultat d' print_r($array)être:

Array
(
    [2] => two
)

Cela ne s'applique pas si vous souhaitez conserver les valeurs filtrées pour une utilisation ultérieure, mais plus ordonné, si vous êtes certain de ne pas le faire.

Alastair
la source
1
Vous devez vérifier si la clé $ key existe dans $ array avant de procéder à unset.
Jarek Jakubowski
3
@JarekJakubowski vous n'avez pas besoin de vérifier si une clé de tableau existe lors de l'utilisation unset(). Aucun avertissement n'est émis si la clé n'existe pas.
Christopher
3

Depuis PHP 5.6, vous pouvez utiliser le ARRAY_FILTER_USE_KEYdrapeau dans array_filter:

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


Sinon, vous pouvez utiliser cette fonction ( de 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);
});


Et voici une version augmentée de la mienne, qui accepte un rappel ou directement les touches:

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


Enfin, vous pouvez également utiliser un simple foreach:

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

Peut-être un excès si vous n'en avez besoin qu'une seule fois, mais vous pouvez utiliser la bibliothèque YaLinqo * pour filtrer les collections (et effectuer d'autres transformations). Cette bibliothèque permet d'exécuter des requêtes de type SQL sur des objets avec une syntaxe fluide. Sa wherefonction accepte un calback avec deux arguments: une valeur et une clé. Par exemple:

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

(La wherefonction renvoie un itérateur, donc si vous avez seulement besoin de répéter foreachune fois la séquence résultante, elle ->toArray()peut être supprimée.)

* développé par mes soins

Athari
la source
1

fonction de filtre de tableau de php:

array_filter ( $array, $callback_function, $flag )

$ array - C'est le tableau d'entrée

$ callback_function - La fonction de rappel à utiliser , Si la fonction de rappel renvoie true , la valeur actuelle du tableau est retournée dans le tableau de résultat.

$ flag - C'est un paramètre facultatif , il déterminera quels arguments sont envoyés à la fonction de rappel. Si ce paramètre est vide, la fonction de rappel prendra comme argument les valeurs du tableau. Si vous souhaitez envoyer la clé du tableau comme argument, utilisez $ flag comme ARRAY_FILTER_USE_KEY . Si vous souhaitez envoyer les clés et les valeurs, vous devez utiliser $ flag comme ARRAY_FILTER_USE_BOTH .

Par exemple: considérons un tableau simple

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

Si vous souhaitez filtrer le tableau en fonction de la clé du tableau , nous devons utiliser ARRAY_FILTER_USE_KEY comme troisième paramètre de la fonction tableau array_filter.

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

Si vous souhaitez filtrer le tableau en fonction de la clé du tableau et de la valeur du tableau , nous devons utiliser ARRAY_FILTER_USE_BOTH comme troisième paramètre de la fonction de tableau array_filter.

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

Exemples de fonctions de rappel:

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

Il produira

Output of $get_key is :Array ( [a] => 1 ) 
Output of $get_both is :Array ( [a] => 1 ) 
prince jose
la source
0

Avec cette fonction, vous pouvez filtrer un tableau multidimensionnel

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

}
user1220713
la source
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);

Sortie de l'exécution

ZOB
la source
0

Solution naïve et laide (mais qui semble être plus rapide)?

Seulement essayé en php 7.3.11 mais une boucle laide semble s'exécuter dans environ un tiers du temps. Des résultats similaires sur un tableau avec quelques centaines de clés. Micro-optimisation, probablement pas utile en RW, mais qui l'a trouvée surprenante et intéressante:

$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
sepiariver
la source
-1
$elements_array = ['first', 'second'];

fonction pour supprimer certains éléments du tableau

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

appeler et imprimer

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

le résultat Array ( [0] => first )

Abdallah Awwad Alkhwaldah
la source
La question concernait le filtrage des clés de tableau et non des valeurs.
poletaew