PHP: Dizi anahtarlarını filtrelemek için array_filter () nasıl kullanılır?


363

Geri arama işlevi array_filter(), anahtarların değil, yalnızca dizinin değerlerini iletir.

Sahip olursam:

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

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

Dizide $my_arrayolmayan tüm anahtarları silmenin en iyi yolu nedir $allowed?

Istenilen çıktı:

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

Değil bir çözüm ama yararlı olabilecek başka bir yaklaşım olduğunu $b = ['foo' => $a['foo'], 'bar' => $a['bar']]sonuçlanacaktır Bu $b['bar']yönelik olacaktır null.
oriadam

Yanıtlar:


322

PHP 5.6 için üçüncü parametre tanıttı array_filter(), flagşunları yapmanız ayarlayabileceğini ARRAY_FILTER_USE_KEYvalue yerine anahtar ile filtreye:

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

Açıkçası bu kadar zarif değil array_intersect_key($my_array, array_flip($allowed)), ancak anahtara karşı rastgele bir test gerçekleştirme ek esnekliği sunuyor, örneğin $alloweddüz dizeler yerine normal ifade desenleri içerebilir.

Ayrıca ARRAY_FILTER_USE_BOTH, değer ve anahtarın filtre işlevinize iletilmesi için de kullanabilirsiniz . İşte ilkine dayanan bir örnek, ancak filtreleme kurallarını $allowedbu şekilde kodlamayı önermediğimi unutmayın :

$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
Lanet olsun, bu özelliğin yazarı olarak bu soruyu
aramalıydım

1
Teşekkürler, bu daha iyiarray_intersect
brzuchal

461

İle array_intersect_keyve array_flip:

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

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

1
Bunun benim çözümümden daha verimli olup olmadığını merak ediyorum? Kesinlikle daha zarif :)
GWW

13
Bu genel bir çözüm değildir, çünkü her bir değerin benzersiz olmasını zorunlu kılar. Düzenleme: üzgünüm .. Çözümü yanlış okudum. İzin verilen tuşları çevirmek iyi bir çözümdür (+1)
Matthew

@GWW: Daha verimli olup olmadığını bilmiyorum, TBH. @konforce: Ne demek istediğini anlayamıyorum. Bir dizide iki özdeş anahtar olamaz, bu nedenle yalnızca $ my_array içinde $ izin verilen mevcut anahtarları döndürür.
Vincent Savard

1
Veya sadece ARRAY_FILTER_USE_KEY kullanın: P
Julien Palard

1
Neden kullanılmalı array_flip? Basitçe $allowedtuşları ile tanımlayın :allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.

43

Aynı şeyi yapmam gerekiyordu, ama array_filteranahtarlarda daha karmaşıktı .

Benzer bir yöntem kullanarak bunu nasıl yaptım.

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

Sonuç:

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

8

İşte bir kapatma kullanarak daha esnek bir çözüm:

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

Çıktılar:

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

Yani fonksiyonda, diğer spesifik testleri yapabilirsiniz.


1
Ben buna “daha ​​esnek” demezdim; kabul edilen çözümden çok daha basittir.
13

Katılıyorum. Daha esnek olurdu, durum daha karmaşıktı.
Bobin

1
Diğer kullanıcılar için sadece geçerek: Bu çözüm, $ my_array öğesinin yinelenen değerlere veya tamsayı veya dize olmayan değerlere sahip olduğu durumla ilgilenmez. Bu yüzden bu çözümü kullanmam.
user23127

2
Filtre mantığını değiştirmenize izin verdiği için bunun daha esnek olduğunu kabul ediyorum. Örneğin, izin verilmeyen anahtarlar dizisini kullandım ve sadece döndüm! İn_array ($ key, $ disallowed).
nfplee

5

Bir diziyi anahtarlarda oluşan bir dizeye göre filtrelemek için bir yöntem arıyorsanız, şunları kullanabilirsiniz:

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

Sonucu print_r($mResult)DİR

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

Düzenli ifadeleri destekleyen bu cevabın uyarlanması

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

Çıktı

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

Cevabınız için teşekkürler. Size stristrfonksiyonun "çalışması" içinde kullanmanın son kullanıcı için bazı varsayımlar yaptığını bildiririm . Belki de kullanıcının düzenli bir ifadeden geçmesine izin vermek daha iyi olur; bu onlara çapa, kelime sınırları ve büyük / küçük harf duyarlılığı gibi bazı şeyler üzerinde daha fazla esneklik sağlar.
maček

Cevabınızın başkalarına yardımcı olabilecek bir uyarlaması ekledim
maček

1
Kesinlikle haklısın, maček, bu regex ile rahat eden kullanıcılar için daha çok yönlü bir yaklaşım. Teşekkürler.
Nicolas Zimmer

5

Kullanırken bir dizinin geçerli anahtarını alma array_filter

Vincent'ın Maček'in sorununa çözümünü nasıl sevdiğime bakılmaksızın, aslında kullanmıyor array_filter. Buraya bir arama motorundan geldiyseniz, belki böyle bir şey arıyorsunuz ( 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';
}

Filtrelediğiniz diziyi geri aramaya referans olarak geçirir. As array_filterartırarak dizinin üzerinde geleneksel olarak yinelerler değil bunu kendiniz ilerlemek zorunda kamu iç işaretçi bu.

Burada önemli olan, dizinizin sıfırlandığından emin olmanız gerektiğidir, aksi takdirde tam ortasından başlayabilirsiniz.

In PHP> = 5.4 Eğer geri arama daha da kısa yapabilir:

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

3

İşte unset () kullanarak daha az esnek bir alternatif :

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

print_r($array)Olmanın sonucu :

Array
(
    [2] => two
)

Bu, filtrelenmiş değerleri daha sonra kullanmak üzere saklamak istemezseniz, ancak daha düzenli olup olmadığınızdan eminseniz geçerli değildir.


1
Ayarlanmadan önce $ dizisinde $ anahtarının anahtarının var olup olmadığını kontrol etmelisiniz.
Jarek Jakubowski

3
@JarekJakubowski, kullanırken bir dizi anahtarının olup olmadığını kontrol etmenize gerek yoktur unset(). Anahtar yoksa hiçbir uyarı yapılmaz.
Christopher

3

PHP 5.6'dan başlayarak, ARRAY_FILTER_USE_KEYbayrağı şurada kullanabilirsiniz array_filter:

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


Aksi takdirde, bu işlevi kullanabilirsiniz ( TestDummy'den ):

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


Ve işte bir geri çağrıyı veya doğrudan anahtarları kabul eden artırılmış bir versiyonum:

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


Son olarak, aynı zamanda basit bir yöntem de kullanabilirsiniz foreach:

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

1

Belki sadece bir kez ihtiyacınız varsa aşırıya kaçma, ancak koleksiyonları filtrelemek (ve diğer dönüşümleri gerçekleştirmek) için YaLinqo kütüphanesini * kullanabilirsiniz . Bu kütüphane akıcı sözdizimine sahip nesneler üzerinde SQL benzeri sorguların oluşturulmasına izin verir. Onun wherebir değeri ve bir anahtar: işlev iki bağımsız değişken içeren bir calback kabul eder. Örneğin:

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

( whereİşlev bir yineleyici döndürür, bu nedenle foreachelde edilen sekans üzerinde yalnızca bir kez yinelemeniz ->toArray()gerekiyorsa kaldırılabilir.)

* benim tarafımdan geliştirildi


1

php dizi filtre fonksiyonu:

array_filter ( $array, $callback_function, $flag )

$ array - Girdi dizisidir

$ callback_function - Kullanılacak geri arama işlevi , Geri arama işlevi true değerini döndürürse değerini , dizideki geçerli değer sonuç dizisine döndürülür.

$ flag - İsteğe bağlı bir parametredir , geri çağrı işlevine hangi bağımsız değişkenlerin gönderileceğini belirler. Bu parametre boşsa, geri çağrı işlevi dizi değerlerini bağımsız değişken olarak alır. Dizi anahtarını bağımsız değişken olarak göndermek istiyorsanız, $ flag öğesini ARRAY_FILTER_USE_KEY olarak kullanın . Hem anahtarları hem de değerleri göndermek istiyorsanız $ flag'ı ARRAY_FILTER_USE_BOTH olarak kullanmalısınız .

Örneğin: Basit diziyi düşünün

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

Diziyi dizi anahtarına göre filtrelemek istiyorsanız, array_filter dizi işlevinin üçüncü parametresi olarak ARRAY_FILTER_USE_KEY kullanmamız gerekir .

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

Diziyi dizi anahtarına ve dizi değerine göre filtrelemek istiyorsanız, array_filter dizi işlevinin üçüncü parametresi olarak ARRAY_FILTER_USE_BOTH kullanmamız gerekir .

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

Örnek Geri Arama işlevleri:

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

Çıktı verecek

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

0

Bu işlevle çok boyutlu bir diziyi filtreleyebilirsiniz

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

Çalışmadan çıktı


0

Saf ve çirkin (ama daha hızlı görünüyor) çözüm?

Sadece php 7.3.11 bunu denedim ama çirkin bir döngü zamanın yaklaşık üçte birinde yürütmek gibi görünüyor. Birkaç yüz tuşa sahip bir dizide benzer sonuçlar. Mikro optimizasyon, muhtemelen RW'de yararlı değildir, ancak şaşırtıcı ve ilginç buldu:

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

dizi öğelerini kaldırma işlevi

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

ara ve yazdır

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

sonuç Array ( [0] => first )


Soru, dizi anahtarlarını değil değerleri filtrelemekti.
poletaew
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.