PHP: Bagaimana cara menggunakan array_filter () untuk memfilter kunci array?

363

Fungsi panggilan balik array_filter()hanya lewat dalam nilai-nilai array, bukan kunci.

Jika saya punya:

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

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

Apa cara terbaik untuk menghapus semua kunci $my_arrayyang tidak ada dalam $allowedarray?

Output yang diinginkan:

$my_array = array("foo" => 1);
maček
sumber
Bukan solusi tapi pendekatan lain yang mungkin berguna adalah untuk $b = ['foo' => $a['foo'], 'bar' => $a['bar']]ini akan menghasilkan $b['bar']be null.
oriadam

Jawaban:

322

PHP 5.6 memperkenalkan parameter ketiga untuk array_filter(), flag, bahwa Anda dapat mengatur untuk ARRAY_FILTER_USE_KEYuntuk menyaring oleh key bukan nilai:

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

Jelas ini tidak seanggun array_intersect_key($my_array, array_flip($allowed)), tetapi memang menawarkan fleksibilitas tambahan untuk melakukan tes sewenang-wenang terhadap kunci, misalnya $alloweddapat berisi pola regex bukan string biasa.

Anda juga dapat menggunakan ARRAY_FILTER_USE_BOTHagar nilai dan kunci diteruskan ke fungsi filter Anda. Berikut adalah contoh yang dibuat berdasarkan yang pertama, tetapi perhatikan bahwa saya tidak akan merekomendasikan penyandian aturan penyaringan menggunakan $allowedcara ini:

$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
sumber
21
Sial, sebagai penulis fitur itu saya harus mencari pertanyaan ini ;-)
Ja͢ck
1
Terima kasih, ini lebih baik daripadaarray_intersect
brzuchal
461

Dengan array_intersect_keydan array_flip:

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

array(1) {
  ["foo"]=>
  int(1)
}
Vincent Savard
sumber
1
Saya ingin tahu apakah ini lebih efisien daripada solusi saya? Jelas lebih elegan :)
GWW
13
Ini bukan solusi umum karena akan mengharuskan setiap nilai unik. Sunting: maaf .. Saya salah membaca solusinya. Membalik tombol yang diizinkan adalah solusi yang baik (+1)
Matius
@ GWW: Saya tidak tahu apakah ini lebih efisien, TBH. @ configce: Saya tidak yakin untuk mengerti maksud Anda. Tidak boleh ada dua kunci identik dalam array, jadi itu hanya akan mengembalikan kunci dalam $ my_array yang hadir dalam $ diizinkan.
Vincent Savard
1
Atau cukup gunakan ARRAY_FILTER_USE_KEY: P
Julien Palard
1
Mengapa menggunakan array_flip? Cukup tentukan $allowedkunci dengan:allowed = array ( 'foo' => 1, 'bar' => 1 );
Yuval A.
43

Saya perlu melakukan hal yang sama, tetapi dengan array_filtertombol yang lebih kompleks .

Begini cara saya melakukannya, menggunakan metode yang serupa.

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

Ini menghasilkan hasilnya:

Array
(
    [three] => val three
    [four] => val four
    [five] => val five
)
Christopher
sumber
8

Berikut ini adalah solusi yang lebih fleksibel menggunakan penutupan:

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

Output:

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

Jadi dalam fungsinya, Anda bisa melakukan tes khusus lainnya.

Gulungan
sumber
1
Saya tidak akan menyebut ini "lebih fleksibel"; rasanya jauh lebih mudah daripada solusi yang diterima juga.
maček
Saya setuju. Akan lebih fleksibel adalah kondisi yang lebih kompleks.
COil
1
Hanya lewat, untuk pengguna lain: Solusi ini tidak berurusan dengan kasus $ my_array memiliki nilai duplikat atau nilai yang bukan bilangan bulat atau string. Jadi saya tidak akan menggunakan solusi ini.
user23127
2
Saya setuju ini lebih fleksibel karena memungkinkan Anda untuk mengubah logika filter. Sebagai contoh, saya menggunakan array kunci yang tidak diizinkan dan hanya mengembalikan! In_array ($ key, $ dilarang).
nfplee
5

Jika Anda mencari metode untuk memfilter array dengan string yang terjadi pada kunci, Anda dapat menggunakan:

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

Hasilnya print_r($mResult)adalah

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

Adaptasi jawaban ini yang mendukung ekspresi reguler

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

Keluaran

Array
(
    [foo] => yes
    [foo2] => yes
    [FooToo] => yes
)
Nicolas Zimmer
sumber
Terima kasih atas jawaban anda. Saya akan menyampaikan kepada Anda bahwa menggunakan stristr"fungsi" fungsi membuat beberapa asumsi untuk pengguna akhir. Mungkin akan lebih baik untuk mengizinkan pengguna untuk menyampaikan ekspresi reguler; ini akan memberi mereka lebih banyak fleksibilitas atas hal-hal tertentu seperti jangkar, batas kata, dan sensitivitas huruf, dll.
maček
Saya telah menambahkan adaptasi jawaban Anda yang mungkin membantu orang lain
maček
1
Anda tentu benar, itu adalah pendekatan yang lebih fleksibel untuk pengguna yang merasa nyaman dengan regex. Terima kasih.
Nicolas Zimmer
5

Cara mendapatkan kunci array saat ini saat menggunakan array_filter

Terlepas dari bagaimana saya suka solusi Vincent untuk masalah Maček, itu tidak benar-benar digunakan array_filter. Jika Anda datang ke sini dari mesin pencari Anda mungkin mencari tempat seperti ini ( 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';
}

Melewati array yang Anda filter sebagai referensi ke callback. Karena array_filtertidak secara konvensional beralih pada array dengan meningkatkan itu pointer internal publik Anda harus memajukannya sendiri.

Yang penting di sini adalah Anda perlu memastikan array Anda disetel ulang, jika tidak Anda bisa mulai tepat di tengahnya.

Dalam PHP> = 5,4 Anda dapat membuat panggilan balik lebih pendek:

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

Berikut alternatif yang kurang fleksibel menggunakan unset () :

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

Hasil dari print_r($array)menjadi:

Array
(
    [2] => two
)

Ini tidak berlaku jika Anda ingin menyimpan nilai yang difilter untuk digunakan nanti tetapi lebih rapi, jika Anda yakin tidak melakukannya.

Alastair
sumber
1
Anda harus memeriksa apakah kunci $ kunci ada dalam $ array sebelum melakukan unset.
Jarek Jakubowski
3
@JarekJakubowski Anda tidak perlu memeriksa apakah kunci array ada saat menggunakan unset(). Tidak ada peringatan yang dikeluarkan jika kunci tidak ada.
Christopher
3

Mulai dari PHP 5.6, Anda dapat menggunakan ARRAY_FILTER_USE_KEYflag di array_filter:

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


Jika tidak, Anda dapat menggunakan fungsi ini ( dari 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);
});


Dan di sini adalah versi augmented milik saya, yang menerima panggilan balik atau langsung kunci:

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


Terakhir namun tidak kalah pentingnya, Anda juga dapat menggunakan yang sederhana foreach:

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

Mungkin berlebihan jika Anda membutuhkannya sekali saja, tetapi Anda dapat menggunakan pustaka YaLinqo * untuk memfilter koleksi (dan melakukan transformasi lainnya). Pustaka ini memungkinkan peforming query seperti SQL pada objek dengan sintaks yang lancar. Fungsinya wheremenerima calback dengan dua argumen: nilai dan kunci. Sebagai contoh:

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

( whereFungsi mengembalikan iterator, jadi jika Anda hanya perlu mengulangi dengan foreachurutan yang dihasilkan sekali, ->toArray()dapat dihapus.)

* Dikembangkan oleh saya

Athari
sumber
1

fungsi filter array dari php:

array_filter ( $array, $callback_function, $flag )

$ array - Ini adalah array input

$ callback_function - Fungsi callback yang digunakan , Jika fungsi callback kembali true , nilai saat ini dari array dikembalikan ke array hasil.

$ flag - Ini adalah parameter opsional , ini akan menentukan argumen apa yang dikirim ke fungsi callback. Jika parameter ini kosong maka fungsi callback akan mengambil nilai array sebagai argumen. Jika Anda ingin mengirim kunci array sebagai argumen, gunakan $ flag sebagai ARRAY_FILTER_USE_KEY . Jika Anda ingin mengirim kedua kunci dan nilai, Anda harus menggunakan $ flag sebagai ARRAY_FILTER_USE_BOTH .

Sebagai Contoh: Pertimbangkan array sederhana

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

Jika Anda ingin memfilter array berdasarkan kunci array , Kita perlu menggunakan ARRAY_FILTER_USE_KEY sebagai parameter ketiga dari array function array_filter.

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

Jika Anda ingin memfilter array berdasarkan kunci array dan nilai array , Kita perlu menggunakan ARRAY_FILTER_USE_BOTH sebagai parameter ketiga dari array function array_filter.

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

Contoh fungsi Panggilan Balik:

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

Ini akan menampilkan

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

Dengan fungsi ini Anda dapat memfilter array multidimensi

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

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

Output dari proses

ZOB
sumber
0

Solusi naif dan jelek (tetapi tampaknya lebih cepat)?

Hanya mencoba ini di php 7.3.11 tetapi loop jelek tampaknya mengeksekusi di sekitar sepertiga dari waktu. Hasil serupa pada array dengan beberapa ratus kunci. Optimalisasi mikro, mungkin tidak berguna di RW, tetapi ternyata mengejutkan dan menarik:

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

berfungsi untuk menghapus beberapa elemen array

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

panggil dan cetak

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

hasil Array ( [0] => first )

Abdallah Awwad Alkhwaldah
sumber
Pertanyaannya adalah tentang memfilter kunci array bukan nilai.
poletaew