web-dev-qa-db-fra.com

Comment effectuer une recherche par clé => valeur dans un tableau multidimensionnel dans PHP

Existe-t-il un moyen rapide d’obtenir tous les sous-tableaux pour lesquels une paire clé-valeur a été trouvée dans un tableau multidimensionnel? Je ne peux pas dire à quelle profondeur le tableau sera.

Exemple de tableau simple:

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

Lorsque je recherche clé = nom et valeur = "cat 1", la fonction doit renvoyer:

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

Je suppose que la fonction doit être récursive pour atteindre le niveau le plus profond.

142
dan

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

Sortie:

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

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

)

Si l'efficacité est importante, vous pouvez l'écrire pour que tous les appels récursifs stockent leurs résultats dans le même tableau temporaire $results plutôt que de les fusionner, comme suit:

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 clé est que search_r prend son quatrième paramètre par référence plutôt que par valeur; l'esperluette & est cruciale.

FYI: Si vous avez une version plus ancienne de PHP, vous devez spécifier la pièce "référence par référence" dans appel à search_r plutôt que dans sa déclaration. C'est-à-dire que la dernière ligne devient search_r($subarray, $key, $value, &$results).

202
John Kugelman

Qu'en est-il de la version SPL ? Cela vous évitera de taper:

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

Ce qui est génial, c’est que fondamentalement, le même code parcourt un répertoire pour vous, en utilisant un RecursiveDirectoryIterator au lieu d’un RecursiveArrayIterator. SPL est le roxor.

Le seul inconvénient de SPL est qu'il est mal documenté sur le Web. Mais plusieurs PHP livres contiennent des détails utiles, en particulier Pro PHP; et vous pouvez probablement google pour plus d'informations, aussi.

69
jared
<?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);

?>

Réf.: http://php.net/manual/en/function.array-filter.php

42
Prasanth Bendra

Nous sommes revenus pour publier cette mise à jour à l'intention de ceux qui recherchent un conseil d'optimisation, en particulier l'excellente réponse de John Kugelman ci-dessus.

Sa fonction affichée fonctionne bien, mais je devais optimiser ce scénario pour gérer un ensemble de résultats de 12 000 lignes. La fonction prenait un temps éternel de 8 secondes pour parcourir tous les enregistrements, waaaaaay trop longtemps.

J'avais simplement besoin de la fonction pour arrêter la recherche et revenir lorsque la correspondance était trouvée. Par exemple, si vous recherchez un customer_id, nous savons que nous n'en avons qu'un dans le jeu de résultats et une fois que nous avons trouvé le customer_id dans le tableau multidimensionnel, nous voulons retourner.

Voici la version optimisée en termes de vitesse (et très simplifiée) de cette fonction, pour tous ceux qui en ont besoin. Contrairement aux autres versions, elle ne peut gérer qu’une seule profondeur de tableau, ne recurse pas et évite la fusion de plusieurs résultats.

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

Cela a amené la tâche de faire correspondre les 12 000 enregistrements à 1,5 seconde. toujours très coûteux mais beaucoup plus raisonnable.

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

Une amélioration mineure à la version rapide.

14
blackmogu

Méfiez-vous des algorithmes de recherche linéaire (les exemples ci-dessus sont linéaires) dans des tableaux multidimensionnels, car ils présentent une complexité croissante car leur profondeur augmente le nombre d'itérations nécessaires pour parcourir le tableau entier. Par exemple:

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

il faudrait au maximum 200 itérations pour trouver ce que vous cherchez (si l'aiguille était à [100] [1]), avec un algorithme approprié.

Les algorithmes linéaires dans ce cas fonctionnent à O(n) (nombre total d’éléments dans l’ensemble du tableau), ce qui est médiocre, un million d’entrées (par exemple un tableau 1000x100x10) prend en moyenne 500 000 itérations pour trouver le aiguille. De plus, que se passerait-il si vous décidiez de modifier la structure de votre tableau multidimensionnel? Et PHP lancerait un algorithme récursif si votre profondeur était supérieure à 100. L'informatique peut faire mieux:

Dans la mesure du possible, utilisez toujours des objets au lieu de tableaux multidimensionnels:

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

et appliquez une interface et une fonction de comparateur personnalisées pour les trier et les trouver:

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

Vous pouvez utiliser uasort() pour utiliser un comparateur personnalisé. Si vous vous sentez aventureux, vous devez implémenter vos propres collections pour que vos objets puissent les trier et les gérer (j'étends toujours ArrayObject pour inclure au minimum une fonction de recherche). .

$arrayObj->uasort("myComp");

Une fois qu’elles sont triées (c’est O (n log n), ce qui est aussi valable que sur des données arbitraires), la recherche binaire peut effectuer l’opération en temps O (log n), c’est-à-dire qu’un million d’entrées ne nécessite que ~ 20 itérations chercher. Pour autant que je sache, la recherche binaire de comparateur personnalisé n'est pas implémentée dans PHP (array_search() utilise un ordre naturel qui fonctionne sur les références d'objet et non sur leurs propriétés), vous devrez l'implémenter vous-même comme je le fais moi-même. faire.

Cette approche est plus efficace (il n'y a plus de profondeur) et plus important encore (si vous imposez la comparabilité à l'aide d'interfaces), car les objets définissent leur mode de tri afin que vous puissiez recycler le code à l'infini. Beaucoup mieux =)

7
mbdxgdb2
$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
Vitalii Fedorenko

Voici la solution:

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

?>
4
Tristan
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
radhe

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
Pramendra Gupta

Ceci est une fonction révisée de celle que John K. a publiée ... Je n'ai besoin que de saisir la clé spécifique du tableau et rien au-dessus.

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
Trevor Lettman

J'avais besoin de quelque chose de similaire, mais pour rechercher un tableau multidimensionnel par valeur ... J'ai pris l'exemple de John et ai écrit

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

J'espère que ça aide quelqu'un :)

2
confiq

Et une autre version qui renvoie la valeur de clé de l’élément de tableau dans lequel la valeur est trouvée (pas de récursivité, optimisée pour la vitesse):

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

Merci à tous ceux qui ont posté ici.

1
Darko Hrgovic
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
Monaem AMINA