168 votes

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

Existe-t-il un moyen rapide d'obtenir tous les sous-réseaux où une paire clé-valeur a été trouvée dans un tableau multidimensionnel ? Je ne peux pas dire quelle sera la profondeur du tableau.

Tableau d'exemples simples :

$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 key=name et value="cat 1", la fonction doit retourner :

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 descendre au niveau le plus profond.

236voto

John Kugelman Points 108754

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 de façon à ce que tous les appels récursifs stockent leurs résultats dans le même fichier temporaire. $results plutôt que de fusionner des tableaux ensemble, comme ceci :

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 crucial.

Pour info : Si vous avez une ancienne version de PHP, vous devez spécifier la partie pass-by-reference dans le champ appelez à search_r plutôt que dans sa déclaration. C'est-à-dire que la dernière ligne devient search_r($subarray, $key, $value, &$results) .

76voto

jared Points 911

Que pensez-vous de la SPL à la place ? Cela vous évitera de devoir 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 le même code va itérer dans 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 livres de PHP entrent dans des détails utiles, en particulier Pro PHP ; et vous pouvez probablement chercher plus d'informations sur Google.

0 votes

Cela fonctionne comme un charme et je compte l'utiliser à nouveau pour des problèmes similaires :D La seule partie bizarre est dans le foreach et l'utilisation de la fonction getSubIterator sur le RecursiveIteratorIterator au lieu de la variable $sub. J'ai d'abord pensé qu'il s'agissait d'une faute de frappe, mais c'est la bonne façon de faire ! Merci Jared.

0 votes

Merci pour la solution. Où obtient-on le "id" ? De $outputArray ?

0 votes

Merci, c'est une solution très simple, mais je ne connais pas les performances .

61voto

Prasanth Bendra Points 9618

Essayez cette solution simple et propre :

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

4 votes

C'est une bonne solution si vous voulez rechercher un tableau qui n'a qu'un seul niveau de profondeur, mais cette question particulière concernait la recherche récursive dans un tableau profond ("la fonction doit être récursive pour descendre au niveau le plus profond").

17voto

stefgosselin Points 5880

Je suis revenu pour poster cette mise à jour pour tous ceux qui ont besoin d'un conseil d'optimisation sur ces réponses, en particulier l'excellente réponse de John Kugelman ci-dessus.

La fonction qu'il a postée fonctionne bien mais j'ai dû optimiser ce scénario pour gérer un jeu de résultats de 12 000 lignes. La fonction prenait éternellement 8 secondes pour parcourir tous les enregistrements, ce qui est beaucoup trop long.

J'avais simplement besoin que la fonction arrête la recherche et revienne lorsque la correspondance était trouvée. Par exemple, si nous recherchons un numéro de client, nous savons qu'il n'y en a qu'un seul dans le jeu de résultats. dans le tableau multidimensionnel, nous voulons retourner.

Voici la version optimisée pour la vitesse (et très simplifiée) de cette fonction, pour tous ceux qui en ont besoin. Contrairement à l'autre version, elle ne peut traiter qu'une seule profondeur de tableau, ne fait pas de récursivité et ne fusionne pas les résultats multiples.

// 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 permis de réduire le temps nécessaire à l'appariement des 12 000 enregistrements à 1,5 seconde. Toujours très coûteux mais beaucoup plus raisonnable.

0 votes

Celui-ci est plus rapide que la réponse de Jhon/Jared (0.0009999275207519) vs (0.0020008087158203) Eh bien ce test est spécifique à mon cas et à mon environnement Je m'en tiens à cela, merci stefgosselin

16voto

blackmogu Points 81
if (isset($array[$key]) && $array[$key] == $value)

Une amélioration mineure de la version rapide.

2 votes

En fait, cela l'empêche de lancer des avertissements lorsque la clé n'est pas définie. Pas si mineur ! -> +1'ed.

2 votes

Je suis d'accord, être capable de jeter un coup d'oeil au journal d'erreurs php pour les erreurs majeures et ne pas être pollué par des avertissements est la meilleure solution à mon avis.

0 votes

Ce n'est pas une solution complète et c'est donc plutôt une "tentative de réponse à un autre message" et "pas une réponse".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X