Cara mencari menurut kunci => nilai dalam array multidimensi di PHP

147

Apakah ada cara cepat untuk mendapatkan semua subarrays di mana pasangan nilai kunci ditemukan dalam array multidimensi? Saya tidak bisa mengatakan seberapa dalam array akan.

Contoh array sederhana:

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);

Ketika saya mencari key = name dan value = "cat 1" fungsi akan kembali:

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);

Saya kira fungsinya harus rekursif untuk turun ke level terdalam.


sumber

Jawaban:

217

Kode:

function search($array, $key, $value)
{
    $results = array();

    if (is_array($array)) {
        if (isset($array[$key]) && $array[$key] == $value) {
            $results[] = $array;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, search($subarray, $key, $value));
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1"));

print_r(search($arr, 'name', 'cat 1'));

Keluaran:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => cat 1
        )

    [1] => Array
        (
            [id] => 3
            [name] => cat 1
        )

)

Jika efisiensi penting, Anda dapat menulisnya sehingga semua panggilan rekursif menyimpan hasilnya dalam $resultsarray sementara yang sama daripada menggabungkan array, seperti:

function search($array, $key, $value)
{
    $results = array();
    search_r($array, $key, $value, $results);
    return $results;
}

function search_r($array, $key, $value, &$results)
{
    if (!is_array($array)) {
        return;
    }

    if (isset($array[$key]) && $array[$key] == $value) {
        $results[] = $array;
    }

    foreach ($array as $subarray) {
        search_r($subarray, $key, $value, $results);
    }
}

Kuncinya ada yang search_rmengambil parameter keempat dengan referensi daripada nilai; ampersand &sangat penting.

FYI: Jika Anda memiliki versi lama dari PHP maka Anda harus menentukan bagian pass-by-referensi dalam panggilan untuk search_rbukan di deklarasi. Artinya, baris terakhir menjadi search_r($subarray, $key, $value, &$results).

John Kugelman
sumber
2
@ JohnKugelman Tidakkah kesalahan jawaban "efisien" keluar jika $keytidak ada dalam array? Bukankah lebih baik melakukannya if (array_key_exists($key, $array) && $array[$key] == $value) {?
Mengejar
1
@JohnKugelman Fungsi ini berfungsi dengan baik tetapi terkadang saya memiliki $valueyang mana nulldan fungsinya tidak berfungsi ... array empty... Bagaimana cara membuat array walaupun $value= null? suka search($array, 'id', null)?
Zagloo
71

Bagaimana dengan versi SPL saja? Ini akan menghemat beberapa ketikan:

// I changed your input example to make it harder and
// to show it works at lower depths:

$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
             1 => array(array('id'=>3,'name'=>"cat 1")),
             2 => array('id'=>2,'name'=>"cat 2")
);

//here's the code:

    $arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));

 foreach ($arrIt as $sub) {
    $subArray = $arrIt->getSubIterator();
    if ($subArray['name'] === 'cat 1') {
        $outputArray[] = iterator_to_array($subArray);
    }
}

Apa yang hebat adalah bahwa pada dasarnya kode yang sama akan beralih melalui direktori untuk Anda, dengan menggunakan RecursiveDirectoryIterator alih-alih RecursiveArrayIterator. SPL adalah raketnya.

Satu-satunya kesal tentang SPL adalah bahwa itu didokumentasikan dengan buruk di web. Tetapi beberapa buku PHP membahas detail yang berguna, khususnya Pro PHP; dan Anda mungkin dapat mencari info lebih lanjut di google.

Jared
sumber
Ini berfungsi seperti pesona dan saya berencana menggunakannya lagi untuk masalah yang sama: D Satu-satunya bagian yang aneh adalah di muka dan menggunakan fungsi getSubIterator pada RecursiveIteratorIterator alih-alih $ variabel sub. Saya pikir itu salah ketik pada awalnya tapi itu cara yang benar! terima kasih Jared.
bchhun
2
Solusi yang luar biasa. Cukup cepat juga!
TaylorOtwell
Terima kasih atas solusinya. Di mana kita mendapatkan "id"? Dari $ outputArray?
trante
Terima kasih, solusi yang sangat mudah, tetapi tidak tahu tentang kinerja ??.
Mahesh.D
cara menghapus elemen yang ditemukan (bisa menjadi sub-array) dari array asli?
Fr0zenFyr
49
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
             1 => array("id"=>2,"name"=>"cat 2"),
             2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
   return ($ar['name'] == 'cat 1');
   //return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});

echo "<pre>";
print_r($arr);

?>

Ref: http://php.net/manual/en/function.array-filter.php

Prasanth Bendra
sumber
4
Ini adalah solusi yang baik jika Anda ingin mencari array yang hanya sedalam satu level, tetapi pertanyaan khusus ini adalah tentang pencarian secara rekursif ke dalam array yang dalam ("fungsi harus bersifat rekursif untuk turun ke level terdalam").
orrd
16

Kembali untuk memposting pembaruan ini bagi siapa saja yang memerlukan tip optimasi untuk jawaban ini, khususnya jawaban hebat John Kugelman di atas.

Fungsi yang diposkannya berfungsi dengan baik, tetapi saya harus mengoptimalkan skenario ini untuk menangani 12.000 hasil baris. Fungsi ini mengambil 8 detik abadi untuk melewati semua catatan, waaaaaay terlalu lama.

Saya hanya perlu fungsi untuk BERHENTI mencari dan kembali ketika kecocokan ditemukan. Yaitu, jika mencari customer_id, kami tahu kami hanya memilikinya di resultset dan begitu kami menemukan customer_id dalam array multidimensi, kami ingin kembali.

Ini adalah versi fungsi ini yang dioptimalkan dengan kecepatan (dan banyak disederhanakan), untuk siapa saja yang membutuhkan. Tidak seperti versi lain, itu hanya dapat menangani hanya satu kedalaman array, tidak berulang dan tidak menggabungkan beberapa hasil.

// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {   
    foreach ($array as $subarray){  
        if (isset($subarray[$key]) && $subarray[$key] == $value)
          return $subarray;       
    } 
}

Ini menurunkan tugas untuk mencocokkan 12 000 catatan menjadi 1,5 detik. Masih sangat mahal tetapi jauh lebih masuk akal.

stefgosselin
sumber
yang ini lebih cepat daripada jawaban Jhon / Jared (0,0009999275207519) vs (0,0020008087158203) .. Ya tes ini khusus untuk kasus dan lingkungan saya .. Saya tetap dengan ini, terima kasih stefgosselin
Awena
14
if (isset($array[$key]) && $array[$key] == $value)

Peningkatan kecil ke versi cepat.

blackmogu
sumber
2
Sebenarnya ini mencegahnya dari memberikan peringatan ketika kunci tidak diatur. Tidak terlalu kecil! -> +1.
stefgosselin
2
setuju, bisa benar-benar melirik log kesalahan php untuk kesalahan besar dan tidak mencemari dengan peringatan adalah cara menurut pendapat saya.
codercake
Ini bukan solusi yang lengkap dan lebih dari "Mencoba Menanggapi Posting Lain" dan "Bukan Jawaban".
mickmackusa
7

Hati-hati dengan algoritma pencarian linear (yang di atas adalah linear) dalam array beberapa dimensi karena mereka telah menambah kompleksitas karena kedalamannya meningkatkan jumlah iterasi yang diperlukan untuk melintasi seluruh array. Misalnya:

array(
    [0] => array ([0] => something, [1] => something_else))
    ...
    [100] => array ([0] => something100, [1] => something_else100))
)

akan membutuhkan paling banyak 200 iterasi untuk menemukan apa yang Anda cari (jika jarum berada di [100] [1]), dengan algoritma yang sesuai.

Algoritma linier dalam hal ini bekerja pada O (n) (memesan jumlah elemen dalam seluruh array), ini buruk, sejuta entri (mis. Array 1000x100x10) akan membutuhkan rata-rata 500.000 iterasi untuk menemukan jarum. Juga apa yang akan terjadi jika Anda memutuskan untuk mengubah struktur array multidimensi Anda? Dan PHP akan mengeluarkan algoritma rekursif jika kedalaman Anda lebih dari 100. Ilmu komputer dapat melakukan lebih baik:

Jika memungkinkan, selalu gunakan objek alih-alih array beberapa dimensi:

ArrayObject(
   MyObject(something, something_else))
   ...
   MyObject(something100, something_else100))
)

dan menerapkan antarmuka dan fungsi komparator kustom untuk mengurutkan dan menemukannya:

interface Comparable {
   public function compareTo(Comparable $o);
}

class MyObject implements Comparable {
   public function compareTo(Comparable $o){
      ...
   }
}

function myComp(Comparable $a, Comparable $b){
    return $a->compareTo($b);
}

Anda dapat menggunakan uasort()untuk menggunakan komparator khusus, jika Anda ingin berpetualang Anda harus mengimplementasikan koleksi Anda sendiri untuk objek yang dapat mengurutkan dan mengelolanya (saya selalu memperluas ArrayObject untuk menyertakan fungsi pencarian setidaknya).

$arrayObj->uasort("myComp");

Setelah mereka diurutkan (uasort adalah O (n log n), yang sama baiknya dengan data yang sewenang-wenang), pencarian biner dapat melakukan operasi dalam waktu O (log n), yaitu satu juta entri hanya membutuhkan ~ 20 iterasi untuk Cari. Sejauh yang saya ketahui pencarian biner komparator kustom tidak diterapkan dalam PHP (array_search() menggunakan pemesanan alami yang bekerja pada referensi objek bukan properti mereka), Anda harus mengimplementasikan ini sendiri seperti yang saya lakukan.

Pendekatan ini lebih efisien (tidak ada lagi kedalaman) dan yang lebih penting universal (dengan asumsi Anda menerapkan komparabilitas menggunakan antarmuka) karena objek menentukan bagaimana mereka diurutkan, sehingga Anda dapat mendaur ulang kode tanpa batas. Jauh lebih baik =)

mbdxgdb2
sumber
Jawaban ini harus benar. Meskipun metode pencarian brute force akan melakukannya, ini jauh lebih sedikit sumber daya yang intensif.
Drew
Perlu dicatat bahwa apa yang Anda sarankan hanya masuk akal jika Anda mencari array yang sama berkali-kali. Dibutuhkan waktu yang jauh lebih lama untuk melalui kesulitan mengurutkannya (O (n log n)) daripada yang dilakukannya untuk hanya melakukan pencarian linear untuk nilai (O (n)). Tapi begitu diurutkan, tentu saja, pencarian biner akan lebih cepat.
orrd
Saya juga harus menambahkan bahwa menggunakan objek bukan array mungkin merupakan abstraksi yang berguna, tetapi Anda juga bisa melakukan pencarian biner pada array jika array diurutkan. Anda tidak perlu menggunakan objek untuk mengurutkan array atau melakukan pencarian biner di atasnya.
orrd
6

Ini solusinya:

<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");

$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;  

?>
Tristan
sumber
5
$result = array_filter($arr, function ($var) {   
  $found = false;
  array_walk_recursive($var, function ($item, $key) use (&$found) {  
    $found = $found || $key == "name" && $item == "cat 1";
  });
  return $found;
});
Vitalii Fedorenko
sumber
3

http://snipplr.com/view/51108/nested-array-search-by-value-or-key/

<?php

//PHP 5.3

function searchNestedArray(array $array, $search, $mode = 'value') {

    foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
        if ($search === ${${"mode"}})
            return true;
    }
    return false;
}

$data = array(
    array('abc', 'ddd'),
    'ccc',
    'bbb',
    array('aaa', array('yyy', 'mp' => 555))
);

var_dump(searchNestedArray($data, 555));
Pramendra Gupta
sumber
3
function in_multi_array($needle, $key, $haystack) 
{
    $in_multi_array = false;
    if (in_array($needle, $haystack))
    {
        $in_multi_array = true; 
    }else 
    {
       foreach( $haystack as $key1 => $val )
       {
           if(is_array($val)) 
           {
               if($this->in_multi_array($needle, $key, $val)) 
               {
                   $in_multi_array = true;
                   break;
               }
           }
        }
    }

    return $in_multi_array;
} 
Radhe
sumber
kasus saya berbeda tetapi mendapat petunjuk dari jawaban Anda.
shyammakwana.me
2

Saya membutuhkan sesuatu yang serupa, tetapi untuk mencari array multidimensi berdasarkan nilai ... Saya mengambil contoh John dan menulis

function _search_array_by_value($array, $value) {
        $results = array();
        if (is_array($array)) {
            $found = array_search($value,$array);
            if ($found) {
                $results[] = $found;
            }
            foreach ($array as $subarray)
                $results = array_merge($results, $this->_search_array_by_value($subarray, $value));
        }
        return $results;
    }

Saya harap ini membantu seseorang :)

confiq
sumber
2

Ini adalah fungsi yang direvisi dari yang diposting John K. ... Saya hanya perlu mengambil kunci spesifik dalam array dan tidak ada yang di atasnya.

function search_array ( $array, $key, $value )
{
    $results = array();

    if ( is_array($array) )
    {
        if ( $array[$key] == $value )
        {
            $results[] = $array;
        } else {
            foreach ($array as $subarray) 
                $results = array_merge( $results, $this->search_array($subarray, $key, $value) );
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
       1 => array(id=>2,name=>"cat 2"),
       2 => array(id=>3,name=>"cat 1"));

print_r(search_array($arr, 'name', 'cat 1'));
Trevor Lettman
sumber
1

Dan versi lain yang mengembalikan nilai kunci dari elemen array di mana nilai ditemukan (tidak ada rekursi, dioptimalkan untuk kecepatan):

// if the array is 
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);

//then 
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) ) 
// instead of Array ( [0] => Array ( [id] => 2 ) )

// search array for specific key = value
function search_array($array, $key, $value) {
  $return = array();   
  foreach ($array as $k=>$subarray){  
    if (isset($subarray[$key]) && $subarray[$key] == $value) {
      $return[$k] = $subarray;
      return $return;
    } 
  }
}

Terima kasih untuk semua yang memposting di sini.

Darko Hrgovic
sumber
1
function findKey($tab, $key){
    foreach($tab as $k => $value){ 
        if($k==$key) return $value; 
        if(is_array($value)){ 
            $find = findKey($value, $key);
            if($find) return $find;
        }
    }
    return null;
}
Monaem AMINA
sumber
2
Bisakah Anda mengembangkan jawaban ini? Kode hanya jawaban tidak menjelaskan apa yang sebenarnya Anda lakukan.
Rich Benner
Harap perbarui pertanyaan Anda dengan tujuan untuk mendidik.
mickmackusa
Ini berfungsi hanya untuk menemukan Key, Ini bekerja untuk saya.
Giovanny Gonzalez
0

Jika Anda ingin mencari berbagai tombol, ini bagus

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            $results[] = $resultArray;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}

Kunci tidak akan menimpa karena setiap set kunci => nilai akan berada dalam array terpisah di array yang dihasilkan.
Jika Anda tidak ingin kunci duplikat, gunakan yang ini

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            foreach($resultArray as $key => $single) {

                $results[$key] = $single;
            }
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}
Pankaj
sumber