Come cercare per valore chiave => in un array multidimensionale in PHP


147

Esiste un modo rapido per ottenere tutti i sottoarray in cui è stata trovata una coppia valore-chiave in un array multidimensionale? Non posso dire quanto sarà profonda la matrice.

Matrice di esempio semplice:

$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 key = name e value = "cat 1" la funzione dovrebbe restituire:

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

Immagino che la funzione debba essere ricorsiva per arrivare al livello più profondo.

Risposte:


217

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

Produzione:

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

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

)

Se l'efficienza è importante, puoi scriverla in modo tale che tutte le chiamate ricorsive memorizzino i loro risultati nello stesso $resultsarray temporaneo anziché unire le matrici insieme, in questo modo:

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 è che search_rprende il suo quarto parametro per riferimento piuttosto che per valore; la e commerciale &è cruciale.

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


2
@JohnKugelman Non verrà visualizzato l'errore di risposta "efficiente" se $keynon esiste nell'array? Non sarebbe meglio fare if (array_key_exists($key, $array) && $array[$key] == $value) {?
Insegui il

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

71

Che ne dici invece della versione SPL ? Ti farà risparmiare un po 'di battitura:

// 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 ripeterà per te una directory, usando un RecursiveDirectoryIterator invece di un RecursiveArrayIterator. SPL è il roxor.

L'unico inconveniente di SPL è che è mal documentato sul web. Ma diversi libri di PHP forniscono alcuni dettagli utili, in particolare Pro PHP; e probabilmente puoi anche cercare su Google ulteriori informazioni.


Funziona come un incantesimo e ho intenzione di usarlo di nuovo per problemi simili: D L'unica parte strana è nella ricerca e usando la funzione getSubIterator su RecursiveIteratorIterator invece della variabile $ sub. All'inizio ho pensato che fosse un refuso ma è la strada giusta! grazie Jared.
bchhun,

2
Soluzione eccezionale. Abbastanza veloce anche!
TaylorOtwell,

Grazie per la soluzione Dove otteniamo l '"id"? Da $ outputArray?
trante,

Grazie, soluzione molto semplice, ma non conosci le prestazioni ??.
Mahesh.D,

come disinserire l'elemento trovato (potrebbe essere un sotto-array) dall'array originale?
Fr0zenFir

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

?>

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


4
Questa è una buona soluzione se si desidera cercare un array con un solo livello di profondità, ma questa particolare domanda riguardava la ricerca ricorsiva in un array profondo ("la funzione deve essere ricorsiva per arrivare al livello più profondo").
or

16

Sono tornato a pubblicare questo aggiornamento per chiunque avesse bisogno di un suggerimento di ottimizzazione su queste risposte, in particolare la grande risposta di John Kugelman in alto.

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

Avevo semplicemente bisogno della funzione di interrompere la ricerca e tornare quando è stata trovata la corrispondenza. Vale a dire, se stiamo cercando un customer_id, sappiamo di averne solo uno nel set di risultati e una volta trovato il customer_id nella matrice multidimensionale, vogliamo tornare.

Ecco la versione ottimizzata della velocità (e molto semplificata) di questa funzione, per chiunque ne abbia bisogno. A differenza di altre versioni, è in grado di gestire solo una profondità di array, non viene utilizzato e consente di unire 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;       
    } 
}

Ciò ha portato il compito di abbinare i 12000 record a 1,5 secondi. Ancora molto costoso ma molto più ragionevole.


questo è più veloce della risposta di Jhon / Jared (0.0009999275207519) vs (0.0020008087158203) .. Beh, questo test è specifico per il mio caso e il mio ambiente .. Sono fedele a questo, grazie stefgosselin
Awena

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

Un piccolo imporvement alla versione veloce.


2
In realtà ciò gli impedisce di emettere avvisi quando la chiave non è impostata. Non così minore! -> + 1'ed.
Stefgosselin,

2
concordato, essere in grado di dare un'occhiata nel registro degli errori di php per errori importanti e non averlo inquinato con avvisi è la strada da percorrere secondo me.
codercake,

Questa non è una soluzione completa e quindi è più un "tentativo di rispondere a un altro post" e "Non una risposta".
Mickmackusa,

7

Prestare attenzione agli algoritmi di ricerca lineare (i precedenti sono lineari) in array multidimensionali in quanto hanno una complessità composta in quanto la profondità aumenta il numero di iterazioni richieste per attraversare l'intero array. Per esempio:

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

impiegherebbe al massimo 200 iterazioni per trovare quello che stai cercando (se l'ago fosse a [100] [1]), con un algoritmo adatto.

Gli algoritmi lineari in questo caso eseguono su O (n) (ordina il numero totale di elementi nell'intero array), questo è scarso, un milione di voci (ad esempio un array 1000x100x10) richiederebbe in media 500.000 iterazioni per trovare l'ago. Inoltre cosa succederebbe se decidessi di cambiare la struttura del tuo array multidimensionale? E PHP darebbe il via a un algoritmo ricorsivo se la tua profondità fosse superiore a 100. L'informatica può fare di meglio:

Laddove possibile, utilizzare sempre oggetti anziché matrici multidimensionali:

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

e applica un'interfaccia e una funzione di confronto personalizzate per ordinare e trovarle:

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()per utilizzare un comparatore personalizzato, se ti senti avventuroso, dovresti implementare le tue raccolte per i tuoi oggetti che possano ordinarli e gestirli (estendo sempre ArrayObject per includere almeno una funzione di ricerca).

$arrayObj->uasort("myComp");

Una volta che sono ordinati (uasort è O (n log n), che è buono quanto supera i dati arbitrari), la ricerca binaria può eseguire l'operazione nel tempo O (log n), cioè un milione di voci richiede ~ 20 iterazioni per ricerca. Per quanto ne so, la ricerca binaria del comparatore personalizzato non è implementata in PHP ( array_search()usa un ordinamento naturale che funziona su riferimenti a oggetti e non sulle loro proprietà), dovresti implementarlo come faccio io.

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


Questa risposta dovrebbe essere corretta. Anche se il metodo di ricerca della forza bruta lo farà, questo richiede molte meno risorse.
Ha

Va notato che ciò che stai suggerendo ha senso solo se stai cercando lo stesso array più volte. Ci vuole molto più tempo per affrontare la difficoltà di ordinarlo (O (n log n)) rispetto a fare semplicemente una ricerca lineare per il valore (O (n)). Ma una volta che è ordinato, certo, allora una ricerca binaria sarebbe più veloce.
or

Dovrei anche aggiungere che l'utilizzo di oggetti anziché di array può essere un'astrazione utile, ma è anche possibile effettuare una ricerca binaria su un array se l'array viene ordinato. Non è necessario utilizzare oggetti per ordinare un array o eseguire una ricerca binaria su di esso.
or

6

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;  

?>

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

il mio caso è diverso ma ho ricevuto un suggerimento dalla tua risposta.
shyammakwana.me,

2

Avevo bisogno di qualcosa di simile, ma per cercare un array multidimensionale per valore ... Ho preso John esempio e 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 aiuti qualcuno :)


2

Questa è una funzione rivista rispetto a quella pubblicata da John K. ... Devo prendere solo la chiave specifica dell'array e niente 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'));

1

E un'altra versione che restituisce il valore chiave dall'elemento dell'array in cui viene trovato il valore (nessuna 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 pubblicato qui.


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
Potresti espandere questa risposta? Le risposte solo al codice non spiegano cosa stai effettivamente facendo.
Rich Benner,

Ti preghiamo di aggiornare la tua domanda con l'intento di educare.
Mickmackusa,

Funziona solo per trovare la chiave, funziona per me.
Giovanny Gonzalez,

0

Se vuoi cercare un array di chiavi questo è buono

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

Le chiavi non verranno sovrascritte perché ogni serie di valori chiave => sarà in una matrice separata nella matrice risultante.
Se non vuoi chiavi duplicate, usa questa

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.