2009-06-19 6 views
133

C'è un modo rapido per ottenere tutti i subarray in cui è stata trovata una coppia di valori chiave in una matrice multidimensionale? Non posso dire quanto profonda sarà la matrice.Come cercare per chiave => valore in una matrice multidimensionale in PHP

semplice esempio array:

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

Quando cerco chiave = nome e il valore = "cat 1" la funzione dovrebbe restituire:

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

Credo che la funzione deve essere ricorsiva a scendere al livello più profondo.

risposta

187

Codice:

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

uscita:

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

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

) 

Se l'efficienza è importante che si potrebbe scrivere in modo tutte le chiamate ricorsive memorizzare i loro risultati nella stessa $results array temporaneo piuttosto che di fondere gli array insieme, così:

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

La chiave è quella search_r prende il suo quarto parametro per riferimento anziché per valore; la e commerciale & è fondamentale.

FYI: Se si dispone di una vecchia versione di PHP poi si deve specificare la parte passaggio per riferimento nel chiamata-search_r piuttosto che nella sua dichiarazione. Cioè, l'ultima riga diventa search_r($subarray, $key, $value, &$results).

+2

@JohnKugelman Wont la "efficiente" risposta errore se '$ key' non esiste in l'array? Non sarebbe meglio fare 'if (array_key_exists ($ key, $ array) && $ array [$ key] == $ valore {{? – Chausser

+1

@JohnKugelman Questa funzione funziona bene ma a volte ho il mio '$ valore' che è' null' e la funzione non funziona ... 'array empty' ... Come avere un array anche se' $ value' = 'null'? come 'search ($ array, 'id', null)'? – Zagloo

68

E invece la versione SPL? E ti risparmiare un po 'di digitazione:

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

La cosa fantastica è che fondamentalmente lo stesso codice sarà scorrere un elenco per voi, utilizzando un RecursiveDirectoryIterator invece di un RecursiveArrayIterator. SPL è il roxor.

L'unico inconveniente di SPL è che è mal documentato sul web. Ma molti libri PHP contengono alcuni dettagli utili, in particolare Pro PHP; e puoi probabilmente anche google per maggiori informazioni.

+0

Funziona come un incantesimo e prevedo di riutilizzarlo per problemi simili: D L'unica parte strana è in foreach e utilizza la funzione getSubIterator sul RecursiveIteratorIterator invece della variabile secondaria $. Ho pensato che fosse un errore di battitura all'inizio, ma è la strada giusta! grazie Jared. – bchhun

+2

Impressionante soluzione. Abbastanza veloce anche! – TaylorOtwell

+0

Grazie per la soluzione. Dove prendiamo l'id? Da $ outputArray? – trante

2

avevo bisogno di qualcosa di simile, ma per la ricerca di array multidimensionale per valore ... Ho preso John esempio e ha scritto

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

Spero che aiuta qualcuno :)

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

A imporvement minore alla versione veloce.

+1

In realtà questo impedisce di lanciare avvertimenti quando la chiave non è impostata. Non così piccolo! -> + 1'ed. – stefgosselin

+2

d'accordo, essere in grado di dare un'occhiata al registro degli errori php per gli errori più gravi e non averlo inquinato con gli avvertimenti è la strada da percorrere secondo me. – codercake

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

il mio caso è diverso ma ho ricevuto un suggerimento dalla tua risposta. –

16

tornati a pubblicare questo aggiornamento per chiunque abbia bisogno di una punta di ottimizzazione su queste risposte, particolarmente grande risposta di John Kugelman sopra.

La sua funzione pubblicata funziona correttamente ma ho dovuto ottimizzare questo scenario per gestire un set di risultati di 12.000 righe. La funzione richiedeva un eterno 8 secondi per passare attraverso tutti i record, waaaaaay troppo a lungo.

Avevo semplicemente bisogno della funzione per FERMARE la ricerca e tornare quando è stata trovata una corrispondenza. Cioè, se si cerca un customer_id, sappiamo che ne abbiamo uno solo nel set di risultati e una volta trovato il customerid in array multidimensionale, vogliamo tornare.

Ecco la versione ottimizzata per la velocità (e molto semplificata) di questa funzione, per chiunque ne abbia bisogno. A differenza di altre versioni, può gestire solo una profondità di array, non ricorre e elimina la fusione di più risultati.

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

Questo ha portato lungo la il compito di abbinare i 12 000 record di 1,5 secondi. Ancora molto costoso ma molto più ragionevole.

+0

questo è più veloce della risposta di Jhon/Jared (0.0009999275207519) vs (0.0020008087158203) .. Beh, questo test è specifico per il mio caso e l'ambiente .. Im attaccare con questo, grazie stefgosselin – Awena

1

e un'altra versione che restituisce il valore della chiave dall'elemento matrice in cui si trova il valore (senza ricorsione, ottimizzato per la velocità):

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

Grazie a tutti coloro che hanno postato qui.

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

Questa è una funzione riveduta da quella pubblicata da John K. Ho bisogno di prendere solo la chiave specifica nell'array e nulla sopra di essa.

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

essere attento di algoritmi di ricerca lineari (Sono lineare) in più matrici tridimensionali come sono composti complessità sua profondità aumenta il numero di iterazioni necessarie per attraversare l'intero array. Ad esempio:

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

avrebbe preso i migliori 200 iterazioni per trovare quello che state cercando (se l'ago erano a [100] [1]), con un algoritmo adatto.

algoritmi lineari in questo caso eseguono su O (n) (ordine numero totale di elementi in intero array), questo è povero, un milione di voci (ad esempio una matrice 1000x100x10) occorrerebbero mediamente 500.000 iterazioni per trovare l'ago. Inoltre cosa succederebbe se decidessi di cambiare la struttura del tuo array multidimensionale? E PHP sarebbe calci fuori un algoritmo ricorsivo se la profondità è stato più di 100. Informatica può fare meglio:

Se possibile, utilizzare sempre oggetti invece di più array bidimensionali:

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

e applicare un comparatore personalizzato l'interfaccia e la funzione di ordinare e li trovano:

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

È possibile utilizzare uasort() di utilizzare un comparatore personalizzato, se vi sentite avventurosi è necessario implementare una tua raccolta per gli oggetti che possono ordinare e gestire t orlo (estendo sempre ArrayObject per includere almeno una funzione di ricerca).

$arrayObj->uasort("myComp"); 

Una volta che essi sono ordinati (uasort è O (n log n), che è buono come si ottiene oltre dati arbitrari), ricerca binaria può fare l'operazione in O (log n), vale a dire un milione le voci richiedono solo ~ 20 iterazioni per la ricerca. Per quanto ne so, la ricerca binaria personalizzata non è implementata in PHP (array_search() utilizza un ordinamento naturale che lavora sui riferimenti agli oggetti e non sulle loro proprietà), dovresti implementarlo come me.

Questo approccio è più efficiente (non esiste più una profondità) e, soprattutto, universale (presupponendo che si imponga la comparabilità utilizzando le interfacce) poiché gli oggetti definiscono il modo in cui sono ordinati, in modo da poter riciclare il codice all'infinito. Molto meglio =)

+0

Questa risposta dovrebbe essere corretta. Anche se il metodo di ricerca forza bruta lo farà, questo è molto meno dispendioso in termini di risorse. – Drew

+0

Va notato che ciò che stai suggerendo ha senso solo se stai cercando lo stesso array molte volte. Ci vuole molto più tempo per risolvere il problema di ordinarlo (O (n log n)) piuttosto che fare semplicemente una ricerca lineare per il valore (O (n)). Ma una volta che è ordinato, certo, allora una ricerca binaria sarebbe più veloce. – orrd

+0

Dovrei anche aggiungere che usare gli oggetti invece degli array può essere un'astrazione utile, ma si può anche fare una ricerca binaria su un array se l'array è ordinato. Non è necessario utilizzare oggetti per ordinare una matrice o effettuare una ricerca binaria su di essa. – orrd

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

?> 

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

+3

Questa è una buona soluzione se si vuole cercare un array che abbia solo un livello profondo, ma questa particolare domanda riguardava la ricerca ricorsiva in un array profondo ("la funzione deve essere ricorsiva per arrivare al livello più profondo"). – orrd

+0

Ottima soluzione, anche per multiuso – Juanra

+0

utilizzare https://gist.github.com/benjamw/1690140 in questi casi –

2

Ecco la soluzione:

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

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

Potresti espandere questa risposta? Le risposte al solo codice non spiegano cosa stai effettivamente facendo. –

+0

Si prega di aggiornare la domanda con l'intento di educare. – mickmackusa