PHP'de çok boyutlu bir dizide anahtar => değer ile nasıl arama yapılır


151

Çok boyutlu bir dizide anahtar değer çiftinin bulunduğu tüm alt dizileri almanın hızlı bir yolu var mı? Dizinin ne kadar derin olacağını söyleyemem.

Basit örnek dizi:

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

Key = name ve value = "cat 1" aradığımda işlev şunu döndürmelidir:

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

Sanırım işlevin en derin seviyeye inmek için yinelemeli olması gerekiyor.

Yanıtlar:


221

Kod:

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

Çıktı:

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

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

)

Verimlilik önemliyse, bunu yazabilirsiniz, böylece tüm özyinelemeli çağrılar $results, dizileri bir araya getirmek yerine sonuçlarını aynı geçici dizide saklar , örneğin:

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

Buradaki anahtar search_r, dördüncü parametresini değere göre değil referansa göre almasıdır; ve işareti &çok önemlidir.

Bilginize: PHP'nin daha eski bir sürümüne sahipseniz, çağrıdasearch_r bildirimde bulunmak yerine referansa göre geçiş bölümünü belirtmeniz gerekir . Yani son satır olur search_r($subarray, $key, $value, &$results).


2
@JohnKugelman Dizide yoksa "verimli" yanıt hatasını $keyistemez misiniz? Yapmak daha iyi olmaz if (array_key_exists($key, $array) && $array[$key] == $value) {mı?
Chase

1
Bu işlev güzel çalışıyor ama bazen ben @JohnKugelman benim $valueolan nullve işlev çalışmaları ... gelmez array empty... Nasıl bir dizi bile var $value= null? gibi search($array, 'id', null)?
Zagloo

71

Bunun yerine SPL versiyonuna ne dersiniz ? Size yazmaktan tasarruf edecek:

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

Harika olan, temelde aynı kodun, bir RecursiveArrayIterator yerine bir RecursiveDirectoryIterator kullanarak sizin için bir dizinde yinelemesidir. SPL, roxor'dur.

SPL ile ilgili tek yanıltıcı şey, web'de kötü bir şekilde belgelenmiş olmasıdır. Ancak birkaç PHP kitabı, özellikle Pro PHP olmak üzere bazı yararlı ayrıntılara girmektedir; ve muhtemelen daha fazla bilgi için Google'da arama yapabilirsiniz.


Bu bir cazibe gibi çalışıyor ve benzer problemler için tekrar kullanmayı planlıyorum: D Tek tuhaf kısım foreach'te ve $ sub değişkeni yerine RecursiveIteratorIterator'da getSubIterator işlevini kullanmak. İlk başta bir yazım hatası olduğunu düşündüm ama doğru yol bu! teşekkürler Jared.
bchhun

2
Harika çözüm. Oldukça hızlı da!
TaylorOtwell

Çözüm için teşekkürler. "Kimliği" nereden alıyoruz? $ OutputArray'den mi?
trante

Teşekkürler, çok basit bir çözüm, ancak performansı bilmiyor musunuz ??
Mahesh.D

bulunan eleman (bir alt dizi olabilir) orijinal diziden nasıl kaldırılır?
Fr0zenFyr

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

?>

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


4
Yalnızca bir seviye derinliğinde bir dizi aramak istiyorsanız bu iyi bir çözümdür, ancak bu özel soru derin bir dizide özyinelemeli olarak arama yapmakla ilgiliydi ("en derin seviyeye inmek için fonksiyonun özyinelemeli olması gerekir").
orrd

16

Bu yanıtlarla ilgili bir optimizasyon ipucuna ihtiyaç duyan herkes için bu güncellemeyi yayınlamak için geri döndük, özellikle de John Kugelman'ın yukarıdaki harika cevabı.

Gönderdiği işlevi iyi çalışıyor ancak 12.000 satırlık bir sonuç kümesini işlemek için bu senaryoyu optimize etmem gerekti. İşlev, tüm kayıtların üzerinden geçmek için sonsuz bir 8 saniye sürüyordu, waaaaaay çok uzun.

Aramayı DURDURMA ve eşleşme bulunduğunda geri dönme fonksiyonuna ihtiyacım vardı. Yani, bir customer_id arıyorsanız, sonuç kümesinde yalnızca bir tane olduğunu biliyoruz ve çok boyutlu dizide customer_id'yi bulduğumuzda geri dönmek istiyoruz.

İhtiyacı olan herkes için bu işlevin hızı optimize edilmiş (ve çok basitleştirilmiş) sürümü burada. Diğer sürümlerden farklı olarak, yalnızca bir dizi derinliğini işleyebilir, tekrarlamaz ve birden çok sonucun birleştirilmesini ortadan kaldırır.

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

Bu, 12.000 kaydı 1.5 saniyeye eşleştirme görevini düşürdü. Yine de çok maliyetli ama çok daha makul.


bu bir vs hızlı Jhon daha / Jared'in cevabı (,0009999275207519) 'dir (,0020008087158203) .. Eh bu test benim durumumda ve çevre .. Im bununla yapışmasını özgü, teşekkür stefgosselin olduğunu
Awena

15
if (isset($array[$key]) && $array[$key] == $value)

Hızlı versiyonda küçük bir gelişme.


2
Aslında bu, anahtar ayarlanmadığında uyarı vermesini engeller. O kadar küçük değil! -> + 1'lendi.
stefgosselin

2
kabul ettim, büyük hatalar için php hata günlüğüne gerçekten göz atabilmek ve onu uyarılarla kirletmemek benim görüşüme göre gitmenin yolu.
codercake

Bu tam bir çözüm değil ve daha çok "Başka Bir Gönderiye Yanıt Verme Girişimi" ve "Yanıt Değil".
mickmackusa

7

Çok boyutlu dizilerdeki doğrusal arama algoritmalarına (yukarıdakiler doğrusaldır) dikkat edin, çünkü derinliği tüm diziyi geçmek için gereken yineleme sayısını artırdığından karmaşık karmaşıklığa sahiptirler. Örneğin:

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

uygun bir algoritma ile aradığınızı bulmak için (iğne [100] [1] 'de olsaydı) en fazla 200 yinelemeyi alırdı.

Bu durumda doğrusal algoritmalar O (n) 'de çalışır (dizideki tüm elemanların sıralı toplam sayısı), bu zayıftır, bir milyon giriş (örneğin 1000x100x10 bir dizi) iğneyi bulmak için ortalama 500.000 yineleme gerektirir. Ayrıca çok boyutlu dizinizin yapısını değiştirmeye karar verirseniz ne olur? Derinliğiniz 100'den fazla olsaydı PHP yinelemeli bir algoritmayı başlatırdı. Bilgisayar bilimi daha iyisini yapabilir:

Mümkün olduğunda, çok boyutlu diziler yerine her zaman nesneleri kullanın:

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

ve bunları sıralamak ve bulmak için özel bir karşılaştırma arayüzü ve işlevi uygulayın:

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

uasort()Özel bir karşılaştırıcı kullanmak için kullanabilirsiniz, maceraperest hissediyorsanız, onları sıralayabilen ve yönetebilen nesneleriniz için kendi koleksiyonlarınızı uygulamalısınız (ArrayObject'i her zaman en azından bir arama işlevi içerecek şekilde genişletirim).

$arrayObj->uasort("myComp");

Bir kez sıralandıktan sonra (uasort, O (n log n) 'dir, ki bu, keyfi veriyi aşması kadar iyidir), ikili arama işlemi O (log n) zamanında yapabilir, yani bir milyon giriş yalnızca ~ 20 iterasyon alır arama. Özel karşılaştırıcı ikili aramanın PHP'de uygulanmadığını bildiğim kadarıyla ( array_search()özelliklerinde değil nesne referanslarında çalışan doğal sıralama kullanır), bunu kendi kendinize uygulamanız gerekir.

Bu yaklaşım daha etkilidir (artık bir derinlik yoktur) ve daha da önemlisi evrenseldir (arayüzleri kullanarak karşılaştırılabilirliği zorunlu kıldığınızı varsayarsak), çünkü nesneler nasıl sıralanacaklarını tanımlar, böylece kodu sonsuza kadar geri dönüştürebilirsiniz. Çok daha iyi =)


Bu cevap doğru olmalı. Kaba kuvvet arama yöntemi bunu yapacak olsa da, bu çok daha az kaynak yoğun.
Drew

Önerdiğiniz şeyin yalnızca aynı diziyi birçok kez arıyorsanız mantıklı olduğuna dikkat edilmelidir. Sıralama zahmetinden geçmek (O (n log n)), (O (n)) değeri için doğrusal bir arama yapmaktan çok daha uzun sürer. Ama bir kez sıralandığında, elbette, ikili arama daha hızlı olacaktır.
orrd

Diziler yerine nesnelerin kullanılmasının yararlı bir soyutlama olabileceğini de eklemeliyim, ancak dizi sıralanırsa bir dizi üzerinde ikili arama da yapabilirsiniz. Bir diziyi sıralamak veya üzerinde ikili arama yapmak için nesneleri kullanmanıza gerek yoktur.
orrd

7

İşte çözüm:

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

?>

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

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

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

Benim durumum farklı ama cevabınızdan bir ipucu var.
shyammakwana.me

2

Benzer bir şeye ihtiyacım vardı, ancak çok boyutlu diziyi değere göre aramak için ... John örneğini aldım ve yazdım

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

Umarım birine yardımcı olur :)


2

Bu, John K.'nin gönderdiği işlevden revize edilmiş bir işlev ... Sadece dizideki belirli anahtarı ve üstündeki hiçbir şeyi almam gerekiyor.

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

1

Ve değerin bulunduğu dizi öğesinden anahtar değerini döndüren başka bir sürüm (özyineleme yok, hız için optimize edilmiş):

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

Burada yazan herkese teşekkürler.


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

2
Bu cevabı biraz daha açabilir misin? Yalnızca kod yanıtları gerçekte ne yaptığınızı açıklamaz.
Rich Benner

Lütfen eğitmek amacıyla sorunuzu güncelleyin.
mickmackusa

Bu sadece Anahtar bulmak için işlevseldir, Bu benim için çalışıyor.
Giovanny Gonzalez

0

Anahtar dizisi aramak istiyorsanız, bu iyidir

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

Her bir anahtar => değer kümesi sonuç dizisinde ayrı bir dizide olacağı için anahtarlar üzerine yazılmaz.
Yinelenen anahtarlar istemiyorsanız, bunu kullanın

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