PHP: Jak używać array_filter () do filtrowania kluczy tablicy?

363

Funkcja zwrotna array_filter()przekazuje tylko wartości tablicy, a nie klucze.

Jeżeli mam:

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

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

Jaki jest najlepszy sposób na usunięcie wszystkich kluczy $my_array, których nie ma w $allowedtablicy?

Pożądane wyjście:

$my_array = array("foo" => 1);
maček
źródło
Nie jest to rozwiązanie, ale innego podejścia, które mogą być użyteczne jest $b = ['foo' => $a['foo'], 'bar' => $a['bar']]to spowoduje $b['bar']BE null.
oriadam

Odpowiedzi:

322

PHP 5.6 wprowadzono trzeci parametr do array_filter(), flag, które można ustawić, aby ARRAY_FILTER_USE_KEYdo filtra przez klucz zamiast wartości:

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

Oczywiście nie jest to tak eleganckie array_intersect_key($my_array, array_flip($allowed)), ale oferuje dodatkową elastyczność wykonywania dowolnego testu na kluczu, np. $allowedMoże zawierać wzorce regularne zamiast zwykłych ciągów.

Możesz także użyć, ARRAY_FILTER_USE_BOTHaby zarówno wartość, jak i klucz zostały przekazane do funkcji filtrowania. Oto wymyślony przykład oparty na pierwszym, ale zauważ, że nie polecam kodowania reguł filtrowania w $allowedten sposób:

$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
źródło
21
Cholera, jako autor tej funkcji powinienem był poszukać tego pytania ;-)
Ja͢ck
1
Dzięki, to jest lepsze niżarray_intersect
brzuchal
461

Z array_intersect_keyi array_flip:

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

array(1) {
  ["foo"]=>
  int(1)
}
Vincent Savard
źródło
1
Jestem ciekaw, czy to jest bardziej wydajne niż moje rozwiązanie? Jest zdecydowanie bardziej elegancki :)
GWW
13
Nie jest to ogólne rozwiązanie, ponieważ wymagałoby, aby każda wartość była niepowtarzalna. Edycja: przepraszam .. Źle odczytałem rozwiązanie. Włączenie dozwolonych klawiszy jest dobrym rozwiązaniem (+1)
Matthew
@GWW: Nie wiem, czy to bardziej wydajne, TBH. @konforce: Nie jestem pewien, czy rozumiem twój punkt widzenia. W tablicy nie mogą znajdować się dwa identyczne klucze, więc zwróci tylko klucze w $ my_array, które są obecne w $ dozwolone.
Vincent Savard
1
Lub po prostu użyj ARRAY_FILTER_USE_KEY: P
Julien Palard
1
Dlaczego warto korzystać array_flip? Wystarczy zdefiniować za $allowedpomocą klawiszy:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.
43

Musiałem zrobić to samo, ale z bardziej złożonymi array_filterkluczami.

Oto jak to zrobiłem, używając podobnej metody.

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

To daje wynik:

Array
(
    [three] => val three
    [four] => val four
    [five] => val five
)
Krzysztof
źródło
8

Oto bardziej elastyczne rozwiązanie z zamknięciem:

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

Wyjścia:

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

Tak więc w funkcji możesz wykonać inne określone testy.

Cewka
źródło
1
Nie nazwałbym tego dokładnie „bardziej elastycznym”; wydaje się również o wiele mniej proste niż zaakceptowane rozwiązanie.
maček
Zgadzam się. Byłoby to bardziej elastyczne, gdyby warunek był bardziej złożony.
Cewka
1
Tylko przechodzę, dla innych użytkowników: To rozwiązanie nie zajmuje się przypadkiem, że $ my_array ma zduplikowane wartości lub wartości, które nie są liczbami całkowitymi ani łańcuchami. Więc nie użyłbym tego rozwiązania.
user23127
2
Zgadzam się, że jest to bardziej elastyczne, ponieważ umożliwia zmianę logiki filtru. Na przykład użyłem tablicy niedozwolonych kluczy i po prostu zwróciłem! In_array ($ key, $ disallowed).
nfplee
5

Jeśli szukasz metody filtrowania tablicy według ciągu występującego w kluczach, możesz użyć:

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

Rezultatem print_r($mResult)jest

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

Dostosowanie tej odpowiedzi, które obsługuje wyrażenia regularne

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

Wynik

Array
(
    [foo] => yes
    [foo2] => yes
    [FooToo] => yes
)
Nicolas Zimmer
źródło
dzięki za odpowiedź. Chciałbym przekazać państwu, że korzystanie stristrz „pracy” tej funkcji czyni pewne założenia dla użytkownika końcowego. Być może lepiej byłoby pozwolić użytkownikowi na przekazanie wyrażenia regularnego; dałoby im to większą elastyczność w odniesieniu do niektórych rzeczy, takich jak kotwice, granice słów i rozróżnianie wielkości liter itp.
maček,
Dodałem adaptację twojej odpowiedzi, która może pomóc innym ludziom
maček
1
Masz rację, maček, jest to bardziej uniwersalne podejście dla użytkowników, którzy czują się komfortowo z regex. Dzięki.
Nicolas Zimmer
5

Jak uzyskać bieżący klucz tablicy podczas używania array_filter

Niezależnie od tego, jak podoba mi się rozwiązanie Vincenta dla problemu Mačka, tak naprawdę nie działa array_filter. Jeśli przyszedłeś tutaj z wyszukiwarki, być może szukasz czegoś takiego ( 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';
}

Przekazuje tablicę, którą filtrujesz, jako odniesienie do wywołania zwrotnego. Ponieważ array_filterkonwencjonalnie nie iteruje się po tablicy, zwiększając jej publiczny wskaźnik wewnętrzny, musisz ją przesuwać samodzielnie.

Ważne jest tutaj, aby upewnić się, że tablica została zresetowana, w przeciwnym razie możesz zacząć dokładnie w jej środku.

W PHP> = 5.4 możesz uczynić callback jeszcze krótszym:

$apples = array_filter($array, function($color) use ($&array) {
  return each($array)['key'] === 'apple';
}
grypa
źródło
3

Oto mniej elastyczna alternatywa przy użyciu unset () :

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

Wynik print_r($array)bycia:

Array
(
    [2] => two
)

Nie ma to zastosowania, jeśli chcesz zachować odfiltrowane wartości do późniejszego użycia, ale bardziej uporządkowane, jeśli masz pewność, że nie.

Alastair
źródło
1
Powinieneś sprawdzić, czy klucz $ key istnieje w tablicy $ przed wykonaniem resetu.
Jarek Jakubowski
3
@JarekJakubowski nie musisz sprawdzać, czy istnieje klucz tablicy podczas używania unset(). Jeśli klucz nie istnieje, nie są generowane żadne ostrzeżenia.
Christopher
3

Począwszy od PHP 5.6, możesz użyć ARRAY_FILTER_USE_KEYflagi w array_filter:

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


W przeciwnym razie możesz użyć tej funkcji ( z 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);
});


A oto moja ulepszona wersja, która akceptuje oddzwonienie lub bezpośrednio klucze:

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


Last but not least, możesz również użyć prostego foreach:

$result = [];
foreach ($my_array as $key => $value) {
    if (in_array($key, $allowed)) {
        $result[$key] = $value;
    }
}
Gras Double
źródło
1

Być może przesada, jeśli potrzebujesz jej tylko raz, ale możesz użyć biblioteki YaLinqo * do filtrowania kolekcji (i wykonywania innych transformacji). Ta biblioteka pozwala na wykonywanie zapytań podobnych do SQL na obiektach o płynnej składni. Jego wherefunkcja akceptuje funkcję calback z dwoma argumentami: wartością i kluczem. Na przykład:

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

( whereFunkcja zwraca iterator, więc jeśli potrzebujesz tylko foreachraz ->toArray()wykonać wynikową sekwencję, możesz ją usunąć.)

* opracowane przeze mnie

Athari
źródło
1

funkcja filtrowania tablicy z php:

array_filter ( $array, $callback_function, $flag )

$ array - To jest tablica wejściowa

$ callback_function - Funkcja zwrotna, której należy użyć, jeśli funkcja zwrotna zwraca true , bieżąca wartość z tablicy jest zwracana do tablicy wynikowej.

Flaga $ - jest to parametr opcjonalny , który określa, jakie argumenty są wysyłane do funkcji zwrotnej. Jeśli ten parametr jest pusty, wówczas funkcja wywołania zwrotnego przyjmuje wartości tablicowe jako argument. Jeśli chcesz wysłać klucz tablicy jako argument, użyj flagi $ jako ARRAY_FILTER_USE_KEY . Jeśli chcesz wysłać zarówno klucze, jak i wartości, powinieneś użyć flagi $ jako ARRAY_FILTER_USE_BOTH .

Na przykład: Rozważ prostą tablicę

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

Jeśli chcesz filtrować tablicę na podstawie klucza tablicy , musimy użyć ARRAY_FILTER_USE_KEY jako trzeciego parametru funkcji tablicy array_filter.

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

Jeśli chcesz filtrować tablicę na podstawie klucza tablicy i wartości tablicy , musimy użyć ARRAY_FILTER_USE_BOTH jako trzeciego parametru funkcji tablicy array_filter.

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

Przykładowe funkcje oddzwaniania:

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

Wyjdzie

Output of $get_key is :Array ( [a] => 1 ) 
Output of $get_both is :Array ( [a] => 1 ) 
książę jose
źródło
0

Dzięki tej funkcji możesz filtrować tablicę wielowymiarową

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

}
użytkownik1220713
źródło
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);

Dane wyjściowe z przebiegu

ZOB
źródło
0

Naiwne i brzydkie (ale wydaje się być szybsze) rozwiązanie?

Próbowałem tego tylko w php 7.3.11, ale brzydka pętla wydaje się działać w około jednej trzeciej czasu. Podobne wyniki dla tablicy zawierającej kilkaset kluczy. Mikrooptymalizacja, prawdopodobnie nieużyteczna w RW, ale była zaskakująca i interesująca:

$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
źródło
-1
$elements_array = ['first', 'second'];

funkcja usuwania niektórych elementów tablicy

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

zadzwoń i wydrukuj

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

wynik Array ( [0] => first )

Abdallah Awwad Alkhwaldah
źródło
Pytanie dotyczyło filtrowania kluczy tablicy, a nie wartości.
poletaew