So suchen Sie nach Schlüssel => Wert in einem mehrdimensionalen Array in PHP

147

Gibt es eine schnelle Möglichkeit, alle Subarrays abzurufen, bei denen ein Schlüsselwertpaar in einem mehrdimensionalen Array gefunden wurde? Ich kann nicht sagen, wie tief das Array sein wird.

Einfaches Beispielarray:

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

Wenn ich nach key = name und value = "cat 1" suche, sollte die Funktion Folgendes zurückgeben:

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

Ich denke, die Funktion muss rekursiv sein, um auf die tiefste Ebene zu gelangen.


quelle

Antworten:

217

Code:

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

Ausgabe:

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

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

)

Wenn Effizienz wichtig ist, können Sie sie so schreiben, dass alle rekursiven Aufrufe ihre Ergebnisse in demselben temporären $resultsArray speichern, anstatt Arrays wie folgt zusammenzuführen:

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

Der Schlüssel dort ist, dass search_rsein vierter Parameter eher als Referenz als als Wert verwendet wird. Das kaufmännische Und &ist entscheidend.

Zu Ihrer Information: Wenn Sie eine ältere Version von PHP haben, müssen Sie den Referenzteil im Aufruf an search_rund nicht in seiner Deklaration angeben . Das heißt, die letzte Zeile wird search_r($subarray, $key, $value, &$results).

John Kugelman
quelle
2
@ JohnKugelman Wird der "effiziente" Antwortfehler $keynicht ausgegeben, wenn er nicht im Array vorhanden ist? Wäre es nicht besser zu tun if (array_key_exists($key, $array) && $array[$key] == $value) {?
Chase
1
@JohnKugelman Diese Funktion funktioniert gut , aber irgendwann habe ich meine , $valuedas ist nullund die Funktion funktioniert nicht ... array empty... Wie ein Array zu haben , auch wenn $value= null? wie search($array, 'id', null)?
Zagloo
71

Wie wäre es stattdessen mit der SPL- Version? Das erspart Ihnen das Tippen:

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

Was großartig ist, ist, dass im Grunde derselbe Code für Sie durch ein Verzeichnis iteriert, indem ein RecursiveDirectoryIterator anstelle eines RecursiveArrayIterator verwendet wird. SPL ist der Roxor.

Das einzige Problem bei SPL ist, dass es im Web schlecht dokumentiert ist. Einige PHP-Bücher enthalten jedoch einige nützliche Details, insbesondere Pro PHP. und Sie können wahrscheinlich auch für weitere Informationen googeln.

jared
quelle
Dies funktioniert wie ein Zauber und ich plane, es für ähnliche Probleme erneut zu verwenden: D Der einzige seltsame Teil ist foreach und die Verwendung der Funktion getSubIterator für den RecursiveIteratorIterator anstelle der Variablen $ sub. Anfangs dachte ich, es sei ein Tippfehler, aber es ist der richtige Weg! danke Jared.
Bchhun
2
Tolle Lösung. Ziemlich schnell auch!
TaylorOtwell
Vielen Dank für die Lösung. Woher bekommen wir die "ID"? Von $ outputArray?
Trante
Danke, sehr einfache Lösung, aber keine Ahnung von Leistung?
Mahesh.D
Wie entferne ich das gefundene Element (könnte ein Sub-Array sein) vom ursprünglichen Array?
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
quelle
4
Dies ist eine gute Lösung, wenn Sie ein Array durchsuchen möchten, das nur eine Ebene tief ist. Bei dieser speziellen Frage ging es jedoch um die rekursive Suche in ein tiefes Array ("Die Funktion muss rekursiv sein, um auf die tiefste Ebene zu gelangen").
Orrd
16

Kam zurück, um dieses Update für alle zu veröffentlichen, die einen Optimierungstipp für diese Antworten benötigen, insbesondere John Kugelmans großartige Antwort oben.

Seine gepostete Funktion funktioniert einwandfrei, aber ich musste dieses Szenario für die Verarbeitung einer Ergebnismenge mit 12.000 Zeilen optimieren. Die Funktion brauchte ewige 8 Sekunden, um alle Aufzeichnungen durchzugehen, waaaaaay zu lange.

Ich brauchte einfach die Funktion, um die Suche zu stoppen und zurückzukehren, wenn eine Übereinstimmung gefunden wurde. Wenn wir also nach einer Kunden-ID suchen, wissen wir, dass wir nur eine in der Ergebnismenge haben, und sobald wir die Kunden-ID im mehrdimensionalen Array gefunden haben, möchten wir zurückkehren.

Hier ist die geschwindigkeitsoptimierte (und stark vereinfachte) Version dieser Funktion für alle Bedürftigen. Im Gegensatz zu anderen Versionen kann es nur eine Array-Tiefe verarbeiten, rekursiert nicht und macht das Zusammenführen mehrerer Ergebnisse überflüssig.

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

Dies brachte die Aufgabe, die 12 000 Datensätze auf 1,5 Sekunden abzugleichen, zum Erliegen. Immer noch sehr teuer, aber viel vernünftiger.

stefgosselin
quelle
Dieser ist schneller als Jhon / Jareds Antwort (0,0009999275207519) vs (0,0020008087158203). Nun, dieser Test ist spezifisch für meinen Fall und meine Umgebung. Ich
bleibe dabei
14
if (isset($array[$key]) && $array[$key] == $value)

Eine kleine Verbesserung der schnellen Version.

Blackmogu
quelle
2
Dies verhindert tatsächlich, dass Warnungen ausgegeben werden, wenn der Schlüssel nicht gesetzt ist. Nicht so klein! -> + 1'ed.
Stefgosselin
2
Ich bin mir einig, dass es meiner Meinung nach der richtige Weg ist, das PHP-Fehlerprotokoll tatsächlich nach schwerwiegenden Fehlern zu durchsuchen und es nicht mit Warnungen verschmutzen zu lassen.
Codercake
Dies ist keine vollständige Lösung, sondern eher ein "Versuch, auf einen anderen Beitrag zu antworten" und "Keine Antwort".
Mickmackusa
7

Achten Sie auf lineare Suchalgorithmen (die oben genannten sind linear) in mehrdimensionalen Arrays, da diese die Komplexität erhöhen, da ihre Tiefe die Anzahl der Iterationen erhöht, die zum Durchlaufen des gesamten Arrays erforderlich sind. Z.B:

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

würde höchstens 200 Iterationen benötigen, um mit einem geeigneten Algorithmus zu finden, wonach Sie suchen (wenn die Nadel bei [100] [1] wäre).

Lineare Algorithmen arbeiten in diesem Fall bei O (n) (Reihenfolge der Gesamtzahl der Elemente im gesamten Array). Dies ist schlecht. Eine Million Einträge (z. B. ein Array von 1000 x 100 x 10) würden durchschnittlich 500.000 Iterationen benötigen, um die Nadel zu finden. Was würde auch passieren, wenn Sie die Struktur Ihres mehrdimensionalen Arrays ändern würden? Und PHP würde einen rekursiven Algorithmus starten, wenn Ihre Tiefe mehr als 100 wäre. Informatik kann es besser machen:

Verwenden Sie nach Möglichkeit immer Objekte anstelle mehrdimensionaler Arrays:

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

und wenden Sie eine benutzerdefinierte Komparatorschnittstelle und -funktion an, um sie zu sortieren und zu finden:

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

Sie können uasort()einen benutzerdefinierten Komparator verwenden. Wenn Sie sich abenteuerlustig fühlen, sollten Sie Ihre eigenen Sammlungen für Ihre Objekte implementieren, die sie sortieren und verwalten können (ich erweitere ArrayObject immer um mindestens eine Suchfunktion).

$arrayObj->uasort("myComp");

Sobald sie sortiert sind (uasort ist O (n log n), was so gut ist, wie es über beliebige Daten geht), kann die binäre Suche die Operation in O (log n) -Zeit ausführen, dh eine Million Einträge dauert nur ~ 20 Iterationen Suche. Soweit mir bekannt ist, ist die benutzerdefinierte Komparator-Binärsuche in PHP nicht implementiert (array_search() verwendet eine natürliche Reihenfolge, die auf Objektreferenzen und nicht auf deren Eigenschaften funktioniert). Sie müssten dies selbst implementieren, wie ich es tue.

Dieser Ansatz ist effizienter (es gibt keine Tiefe mehr) und vor allem universell (vorausgesetzt, Sie erzwingen die Vergleichbarkeit mithilfe von Schnittstellen), da Objekte definieren, wie sie sortiert werden, sodass Sie den Code unbegrenzt recyceln können. Viel besser =)

mbdxgdb2
quelle
Diese Antwort sollte richtig sein. Obwohl die Brute-Force-Suchmethode dies tut, ist dies viel weniger ressourcenintensiv.
Drew
Es ist zu beachten, dass das, was Sie vorschlagen, nur dann sinnvoll ist, wenn Sie dasselbe Array mehrmals durchsuchen. Das Sortieren (O (n log n)) dauert viel länger als das einfache Durchsuchen des Werts (O (n)). Aber sobald es sortiert ist, wäre eine binäre Suche sicher schneller.
Orrd
Ich sollte auch hinzufügen, dass die Verwendung von Objekten anstelle von Arrays eine nützliche Abstraktion sein kann, aber Sie könnten auch eine binäre Suche in einem Array durchführen, wenn das Array sortiert ist. Sie müssen keine Objekte verwenden, um ein Array zu sortieren oder eine binäre Suche durchzuführen.
Orrd
6

Hier ist die Lösung:

<?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
quelle
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
quelle
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
quelle
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
quelle
Mein Fall ist anders, aber ich habe einen Hinweis von Ihrer Antwort erhalten.
Shyammakwana.me
2

Ich brauchte etwas Ähnliches, aber um nach mehrdimensionalen Arrays nach Wert zu suchen ... Ich nahm Johns Beispiel und schrieb

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

Ich hoffe es hilft jemandem :)

confiq
quelle
2

Dies ist eine überarbeitete Funktion von der, die John K. gepostet hat ... Ich muss nur den spezifischen Schlüssel im Array und nichts darüber greifen.

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
quelle
1

Und eine andere Version, die den Schlüsselwert von dem Array-Element zurückgibt, in dem sich der Wert befindet (keine Rekursion, optimiert für Geschwindigkeit):

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

Vielen Dank an alle, die hier gepostet haben.

Darko Hrgovic
quelle
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
quelle
2
Könnten Sie diese Antwort erweitern? Nur-Code-Antworten erklären nicht, was Sie tatsächlich tun.
Rich Benner
Bitte aktualisieren Sie Ihre Frage mit der Absicht zu erziehen.
Mickmackusa
Dies funktioniert nur, um den Schlüssel zu finden. Dies funktioniert für mich.
Giovanny Gonzalez
0

Wenn Sie nach einer Reihe von Schlüsseln suchen möchten, ist dies gut

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

Schlüssel werden nicht überschrieben, da sich jeder Satz von Schlüssel => -Werten in einem separaten Array im resultierenden Array befindet.
Wenn Sie keine doppelten Schlüssel möchten, verwenden Sie diesen

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
quelle