PHP: Come utilizzare array_filter () per filtrare le chiavi dell'array?


363

La funzione di richiamata in array_filter() passa solo nei valori dell'array, non nelle chiavi.

Se ho:

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

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

Qual è il modo migliore per eliminare tutte le chiavi $my_arrayche non sono nella $allowedmatrice?

Uscita desiderata:

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

Non è una soluzione, ma un altro approccio che potrebbe essere utile è quello di $b = ['foo' => $a['foo'], 'bar' => $a['bar']]questo si tradurrà in $b['bar']BE null.
Oriadam,

Risposte:


322

PHP 5.6 ha introdotto un terzo parametro su array_filter(), flagche puoi impostare ARRAY_FILTER_USE_KEYper filtrare in base alla chiave anziché al valore:

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

Chiaramente questo non è elegante come array_intersect_key($my_array, array_flip($allowed)), ma offre la flessibilità aggiuntiva di eseguire un test arbitrario contro la chiave, ad esempio $allowedpotrebbe contenere schemi regex invece di semplici stringhe.

Puoi anche usare ARRAY_FILTER_USE_BOTHper far passare sia il valore che la chiave alla tua funzione filtro. Ecco un esempio inventivo basato sul primo, ma tieni presente che non consiglierei la codifica delle regole di filtro in $allowedquesto modo:

$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
Accidenti, come autore di quella caratteristica avrei dovuto cercare questa domanda ;-)
Ja͢ck

1
Grazie, è meglio diarray_intersect
brzuchal

461

Con array_intersect_keye array_flip:

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

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

1
Sono curioso di sapere se questo è più efficiente della mia soluzione però? È decisamente più elegante :)
GWW,

13
Questa non è una soluzione generale perché impone che ogni valore sia unico. Modifica: scusa .. Ho letto male la soluzione. Lanciare le chiavi consentite è una buona soluzione (+1)
Matteo,

@GWW: Non so se sia più efficiente, TBH. @konforce: non sono sicuro di capire il tuo punto. Non ci possono essere due chiavi identiche in un array, quindi restituirà solo le chiavi in ​​$ my_array presenti in $ consentite.
Vincent Savard,

1
O semplicemente usa ARRAY_FILTER_USE_KEY: P
Julien Palard,

1
Perché l'uso array_flip? Basta definire i $allowedtasti con:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.

43

Avevo bisogno di fare lo stesso, ma con un aspetto più complesso array_filter sui tasti.

Ecco come l'ho fatto, usando un metodo simile.

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

Questo produce il risultato:

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

8

Ecco una soluzione più flessibile che utilizza una chiusura:

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

Uscite:

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

Quindi, nella funzione, puoi fare altri test specifici.


1
Non lo definirei esattamente "più flessibile"; sembra molto meno semplice della soluzione accettata.
1313

Sono d'accordo. Sarebbe più flessibile se la condizione fosse più complessa.
Bobina

1
Solo di passaggio, per altri utenti: questa soluzione non tratta il caso in cui $ my_array abbia valori duplicati o valori che non sono numeri interi o stringhe. Quindi non userei questa soluzione.
user23127

2
Sono d'accordo che sia più flessibile in quanto consente di modificare la logica del filtro. Ad esempio, ho usato un array di chiavi non consentite e ho semplicemente restituito! In_array ($ key, $ disallowed).
nfplee,

5

Se stai cercando un metodo per filtrare un array in base a una stringa presente nelle chiavi, puoi utilizzare:

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

Il risultato di print_r($mResult)è

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

Un adattamento di questa risposta che supporta le espressioni regolari

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

Produzione

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

grazie per la tua risposta. Sottolineo che l'utilizzo stristrall'interno del "lavoro" della funzione sta facendo alcune ipotesi per l'utente finale. Forse sarebbe meglio consentire all'utente di passare un'espressione regolare; questo darebbe loro maggiore flessibilità su certe cose come ancore, confini di parole e maiuscole / minuscole, ecc.
maček

Ho aggiunto un adattamento della tua risposta che potrebbe aiutare altre persone
maček

1
Hai certamente ragione, maček, questo è un approccio più versatile per gli utenti che si sentono a proprio agio con regex. Grazie.
Nicolas Zimmer

5

Come ottenere la chiave corrente di un array durante l'utilizzo array_filter

Indipendentemente da come mi piace la soluzione di Vincent per il problema di Maček, in realtà non la usa array_filter. Se sei venuto qui da un motore di ricerca, forse dove cercavi qualcosa del genere ( 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';
}

Passa l'array che stai filtrando come riferimento al callback. Dal momento array_filterche non iterazione convenzionale sull'array aumentando il puntatore interno pubblico, è necessario avanzare da soli.

La cosa importante qui è che devi assicurarti che l'array sia ripristinato, altrimenti potresti iniziare proprio nel mezzo di esso.

In PHP> = 5.4 potresti rendere il callback ancora più breve:

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

3

Ecco un'alternativa meno flessibile usando unset () :

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

Il risultato di print_r($array)essere:

Array
(
    [2] => two
)

Questo non è applicabile se si desidera mantenere i valori filtrati per un uso successivo, ma più ordinato, se si è certi di non farlo.


1
Dovresti verificare se la chiave $ key esiste in $ array prima di fare unset.
Jarek Jakubowski il

3
@JarekJakubowski non è necessario verificare se esiste una chiave di array durante l'utilizzo unset(). Non viene emesso alcun avviso se la chiave non esiste.
Christopher,

3

A partire da PHP 5.6, puoi usare il ARRAY_FILTER_USE_KEYflag in array_filter:

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


Altrimenti, è possibile utilizzare questa funzione ( da 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);
});


Ed ecco una mia versione aumentata, che accetta un callback o direttamente i tasti:

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


Ultimo ma non meno importante, puoi anche usare un semplice foreach:

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

1

Forse un eccesso se ne hai bisogno solo una volta, ma puoi usare la libreria YaLinqo * per filtrare le raccolte (ed eseguire qualsiasi altra trasformazione). Questa libreria consente di eseguire query simil-SQL su oggetti con sintassi fluida. La sua wherefunzione accetta un calback con due argomenti: un valore e una chiave. Per esempio:

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

(La wherefunzione restituisce un iteratore, quindi se hai bisogno di iterare foreachuna sola volta con la sequenza risultante, ->toArray()puoi rimuoverla.)

* sviluppato da me


1

funzione filtro array da php:

array_filter ( $array, $callback_function, $flag )

$ array - È l'array di input

$ callback_function - La funzione di callback da usare , Se la funzione di callback ritorna true , il valore corrente dall'array viene restituito nell'array dei risultati.

$ flag - È un parametro facoltativo , determinerà quali argomenti vengono inviati alla funzione di callback. Se questo parametro è vuoto, la funzione di callback prenderà i valori dell'array come argomento. Se si desidera inviare la chiave dell'array come argomento, utilizzare $ flag come ARRAY_FILTER_USE_KEY . Se vuoi inviare sia chiavi che valori, dovresti usare $ flag come ARRAY_FILTER_USE_BOTH .

Ad esempio: considerare un array semplice

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

Se si desidera filtrare l'array in base alla chiave dell'array , è necessario utilizzare ARRAY_FILTER_USE_KEY come terzo parametro della funzione dell'array array_filter.

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

Se si desidera filtrare l'array in base alla chiave dell'array e al valore dell'array , è necessario utilizzare ARRAY_FILTER_USE_BOTH come terzo parametro della funzione array array_filter.

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

Funzioni di richiamata di esempio:

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

Verrà emesso

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

0

Con questa funzione è possibile filtrare un array multidimensionale

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

Uscita da corsa


0

Soluzione ingenua e brutta (ma sembra essere più veloce)?

Ci ho provato solo in php 7.3.11, ma un brutto loop sembra eseguirsi in circa un terzo delle volte. Risultati simili su un array con poche centinaia di chiavi. Micro-ottimizzazione, probabilmente non utile in RW, ma lo ha trovato sorprendente 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'];

funzione per rimuovere alcuni elementi dell'array

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

chiama e stampa

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

il risultato Array ( [0] => first )


La domanda riguardava il filtraggio delle chiavi dell'array e non dei valori.
poletaew,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.