Jak wyszukiwać według klucza= > wartości w wielowymiarowej tablicy w PHP

Czy Jest jakiś szybki sposób, aby uzyskać wszystkie podparyski, w których znaleziono parę wartości klucza w tablicy wielowymiarowej? Nie mogę powiedzieć, jak głęboka będzie tablica.

Prosta przykładowa tablica:

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

Gdy poszukuję klucza = nazwa i wartość = "cat 1" funkcja powinna zwracać:

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

Myślę, że funkcja musi być rekurencyjna, aby zejść do najgłębszego poziomu.

Author: Aaron Harun, 2009-06-19

14 answers

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

Wyjście:

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

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

)

Jeśli wydajność jest ważna, możesz ją zapisać tak, aby wszystkie wywołania rekurencyjne przechowywały swoje wyniki w tej samej tymczasowej tablicy $results, a nie łączyły się ze sobą, tak jak:

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

Klucz jest taki, że search_r przyjmuje swój czwarty parametr przez odniesienie, a nie przez wartość; ampersand & jest kluczowy.

FYI: jeśli masz starszą wersję PHP, musisz podać część pass-by-reference w wywołaniu do search_r, a nie w deklaracji. Oznacza to, że ostatni wiersz staje się search_r($subarray, $key, $value, &$results).

 192
Author: John Kugelman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-05 15:39:13

Może zamiast wersji SPL ? Oszczędzi ci to pisania:

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

Wspaniałe jest to, że w zasadzie ten sam kod będzie iteracją przez katalog dla ciebie, używając RecursiveDirectoryIterator zamiast RecursiveArrayIterator. SPL to roxor.

Jedyną wadą SPL jest to, że jest ona źle udokumentowana w Internecie. Ale kilka książek PHP idzie w kilka przydatnych szczegółów, szczególnie Pro PHP; i prawdopodobnie można google Więcej informacji, zbyt.

 68
Author: jared,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-19 19:26:52
<?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

 37
Author: Prasanth Bendra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-17 10:25:28

Wróciłem, aby opublikować tę aktualizację dla każdego, kto potrzebuje porady optymalizacyjnej na temat tych odpowiedzi, zwłaszcza świetnej odpowiedzi Johna Kugelmana powyżej.

Jego opublikowana funkcja działa dobrze, ale musiałem zoptymalizować ten scenariusz do obsługi 12 000 wierszy resultset. Funkcja trwała 8 sekund, aby przejść przez wszystkie rekordy, waaaaaay zbyt długo.

Po prostu potrzebowałem funkcji, aby zatrzymać wyszukiwanie i powrócić po znalezieniu dopasowania. Ie, jeśli szukamy customer_id, wiemy, że mamy tylko jeden w resultset i gdy znajdziemy customer_id w macierz wielowymiarowa, chcemy zwrócić.

Oto zoptymalizowana (i znacznie uproszczona) wersja tej funkcji, dla każdego potrzebującego. W przeciwieństwie do innych wersji, może obsługiwać tylko jedną głębokość tablicy, nie rekurencyjnie i nie łączy wielu wyników.

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

To obniżyło zadanie dopasowania 12 000 rekordów do 1,5 sekundy. Wciąż bardzo kosztowne, ale o wiele bardziej rozsądne.

 16
Author: stefgosselin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-10 07:02:53
if (isset($array[$key]) && $array[$key] == $value)

Drobna zmiana do szybkiej wersji.

 14
Author: blackmogu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-30 16:37:53

Należy uważać na Algorytmy wyszukiwania liniowego (powyższe są liniowe) w tablicach wielowymiarowych, ponieważ mają one zwiększoną złożoność, ponieważ jej głębokość zwiększa liczbę iteracji wymaganych do przemierzenia całej tablicy. Eg:

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

Zajmie co najwyżej 200 iteracji, aby znaleźć to, czego szukasz (jeśli igła byłaby w [100][1]), z odpowiednim algorytmem.

Algorytmy liniowe w tym przypadku wykonują w O (n) (kolejność całkowita liczba elementów w całej tablicy), jest to słabe, milion wpisów (np. tablica 1000x100x10) zajęłoby średnio 500 000 iteracji, aby znaleźć igłę. Co by się stało, gdybyś zdecydował się zmienić strukturę swojej wielowymiarowej tablicy? A PHP wyrzuciłby algorytm rekurencyjny, gdyby twoja głębokość była większa niż 100. Informatyka może być lepsza:

Tam, gdzie to możliwe, Zawsze używaj obiektów zamiast tablic wielowymiarowych:

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

I zastosować niestandardowy interfejs i funkcję komparatora do sortowania i wyszukiwania oni:

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

Możesz użyć uasort(), aby wykorzystać Niestandardowy komparator, jeśli czujesz się poszukiwany, powinieneś zaimplementować własne kolekcje dla swoich obiektów, które mogą je sortować i zarządzać (zawsze rozszerzam ArrayObject, aby przynajmniej zawierała funkcję wyszukiwania).

$arrayObj->uasort("myComp");

Po ich posortowaniu (uasort jest O (n log n) , co jest tak dobre, jak pobiera arbitralne dane), wyszukiwanie binarne może wykonać operację w czasie O (log n), tzn. milion wpisów zajmuje tylko ~20 iteracji do przeszukiwania. Jako o ile wiem, że niestandardowe wyszukiwanie binarne w komparatorze nie jest zaimplementowane w PHP (array_search() używa naturalnego porządkowania, które działa na obiektach, a nie na ich właściwościach), musisz to zaimplementować tak samo jak ja.

To podejście jest bardziej efektywne (nie ma już głębi) i co ważniejsze uniwersalne (zakładając, że wymusisz porównywalność za pomocą interfejsów), ponieważ obiekty definiują sposób ich sortowania, dzięki czemu możesz przetwarzać kod w nieskończoność. Dużo lepiej =)

 7
Author: mbdxgdb2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-15 04:16:06
$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;
});
 5
Author: Vitalii Fedorenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-25 15:23:11

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
Author: Pramendra Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-25 01:42:43
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;
} 
 3
Author: radhe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-23 18:09:08

Potrzebowałem czegoś podobnego, ale do wyszukiwania wielowymiarowej tablicy według wartości... Wziąłem przykład Johna i napisałem

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

Mam nadzieję, że komuś to pomoże:)

 2
Author: confiq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-09 11:05:28

Jest to poprawiona funkcja z tej, którą opublikował Jan K... Muszę chwycić tylko konkretny klucz w tablicy i nic ponad nią.

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'));
 2
Author: Trevor Lettman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-09 17:14:46

Oto rozwiązanie:

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

?>
 2
Author: Tristan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-16 07:16:04

I inna wersja, która zwraca wartość klucza z elementu tablicy, w którym znajduje się wartość (bez rekurencji, zoptymalizowana pod kątem szybkości):

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

Dzięki wszystkim, którzy tu zamieścili.

 1
Author: Darko Hrgovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-22 00:31:58
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;
}
 1
Author: Monaem AMINA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-08 09:30:17