PHP - recherche une entrée par propriété d'objet à partir d'un tableau d'objets


174

Le tableau ressemble à:

[0] => stdClass Object
        (
            [ID] => 420
            [name] => Mary
         )

[1] => stdClass Object
        (
            [ID] => 10957
            [name] => Blah
         )
...

Et j'ai une variable entière appelée $v.

Comment pourrais-je sélectionner une entrée de tableau qui a un objet où la IDpropriété a la $vvaleur?

Réponses:


189

Soit vous itérez le tableau, en recherchant l'enregistrement particulier (ok dans une recherche unique), soit vous construisez un hashmap en utilisant un autre tableau associatif.

Pour le premier, quelque chose comme ça

$item = null;
foreach($array as $struct) {
    if ($v == $struct->ID) {
        $item = $struct;
        break;
    }
}

Voir cette question et les réponses suivantes pour plus d'informations sur cette dernière - Référence du tableau PHP par plusieurs index


3
il n'est pas nécessaire de définir $ item sur null.
dAm2K

32
Oups, ça y est :) C'est au cas où l'élément recherché ne serait pas dans le tableau. Alternativement, vous pouvez utiliser isset($item)mais je préfère initialiser correctement les variables
Phil

3
Pour ceux d'entre vous avec des valeurs de clé définies sur des chaînes, utilisezif($v == $struct["ID"]){...
wbadart

67

YurkamTim a raison. Il n'a besoin que d'une modification:

Après la fonction ($), vous avez besoin d'un pointeur vers la variable externe par "use (& $ viewedValue)" et vous pouvez ensuite accéder à la variable externe. Vous pouvez également le modifier.

$neededObject = array_filter(
    $arrayOfObjects,
    function ($e) use (&$searchedValue) {
        return $e->id == $searchedValue;
    }
);

2
Vous avez raison sur la modification et c'est une sorte de méthode soignée, mais j'ai testé la vitesse par rapport à l'itération à travers l'objet - vous-même, car comme @phil l'a souligné, array_filter le fait aussi - et cette méthode en prend environ cinq fois plus longtemps. Mon objet de test n'est pas grand, donc cela pourrait être encore pire.
Nicolai

9
Le &n'est pas requis lors de l'importation $searchedValuedans l'étendue de fermeture. Le &est utilisé pour créer une référence qui n'est nécessaire que si elle $searchedValuea été modifiée à l'intérieur de la fermeture.
Stefan Gehrig

C'est super. Je ne savais pas que PHP pouvait faire des choses comme ça. Je pensais globalque la seule utilisation était de partager des données dans des fonctions! Mais c'est dommage si c'est vraiment lent. :(
NoOne

13
TS a demandé une seule entrée, ce code renvoie un tableau.
Pavel Vlasov

57
$arr = [
  [
    'ID' => 1
  ]
];

echo array_search(1, array_column($arr, 'ID')); // prints 0 (!== false)

3
Je ne sais pas pourquoi ce n'est pas la réponse préférée. Est-ce parce que vous appelez deux fonctions?
doz87

1
Je pense que j'étais trop tard pour la fête;) Sa pénurie et sa lisibilité sans boucles ni pauses le rendraient raisonnable. Mais je ne l'ai pas encore évalué. Vous avez beaucoup d'options en PHP pour y parvenir.
Tim

3
Solution très élégante. Fonctionne également avec un tableau d'objets en PHP 7. Pour PHP 5: array_search ($ object-> id, array_map (function ($ object) {return $ object-> id;}, $ objects)); Pour PHP 7: array_search ($ object-> id, array_column ($ objects, 'id'));
Mike

3
Ce n'est pas la réponse préférée car op demande un tableau d'objets et cette réponse ne gère que des tableaux purs.
Dwza

8
ce n'est pas correct. ce code gère un tableau d'objets / tableaux non plats
Tim

31

J'ai trouvé une solution plus élégante ici . Adapté à la question, il peut ressembler à:

$neededObject = array_filter(
    $arrayOfObjects,
    function ($e) use ($searchedValue) {
        return $e->id == $searchedValue;
    }
);

16
+1 mais array_filterrenvoie un tableau et ne s'arrête pas à la première valeur trouvée.
Carlos Campderrós

4
Il ne reconnaît pas $searchedValueà l'intérieur de la fonction. Mais à l'extérieur c'est.
M. Ahmad Zafar

4
Pour commencer, ce code ne fonctionne pas tel quel en $searchedValuedehors de la portée de fermeture. Deuxièmement, comment pensez-vous que ces méthodes de tableau fonctionnent? Ils font tous une boucle sur le tableau en interne
Phil

1
À l'époque des multi cœurs, cela - dans d'autres environnements de programmation malheureusement - pourrait être traité en parallèle, la boucle ci-dessus pas nécessairement
FloydThreepwood

3
Pour utiliser $searchedValueneed writefunction ($e) use ($searchedValue) {
Vilintritenmert

20

L'utilisation de array_column pour réindexer vous fera gagner du temps si vous devez rechercher plusieurs fois:

$lookup = array_column($arr, NULL, 'id');   // re-index by 'id'

Ensuite, vous pouvez simplement $lookup[$id]à volonté.


3
C'était la réponse la plus étonnante, même si ce n'est pas la plus intuitive ...
Thiago Natanael

11
class ArrayUtils
{
    public static function objArraySearch($array, $index, $value)
    {
        foreach($array as $arrayInf) {
            if($arrayInf->{$index} == $value) {
                return $arrayInf;
            }
        }
        return null;
    }
}

L'utiliser comme vous le souhaitez serait quelque chose comme:

ArrayUtils::objArraySearch($array,'ID',$v);

9

Essayer

$entry = current(array_filter($array, function($e) use($v){ return $e->ID==$v; }));

exemple de travail ici


1
Très très utile! Merci mec!
Fernando León

ça ne s'arrêtera pas au premier élément trouvé, n'est-ce pas?
yaugenka

7

Correction d'une petite erreur du @YurkaTim , votre solution fonctionne pour moi mais en ajoutant use:

Pour utiliser $searchedValue, à l'intérieur de la fonction, une solution peut être use ($searchedValue)après les paramètres de la fonction function ($e) HERE.

la array_filterfonction ne retourne que $neededObjectsi la condition au retour esttrue

Si $searchedValueest une chaîne ou un entier:

$searchedValue = 123456; // Value to search.
$neededObject = array_filter(
    $arrayOfObjects,
    function ($e) use ($searchedValue) {
        return $e->id == $searchedValue;
    }
);
var_dump($neededObject); // To see the output

Si $searchedValuec'est un tableau où nous devons vérifier avec une liste:

$searchedValue = array( 1, 5 ); // Value to search.
$neededObject  = array_filter(
    $arrayOfObjects,
    function ( $e ) use ( $searchedValue ) {
        return in_array( $e->term_id, $searchedValue );
    }
);
var_dump($neededObject); // To see the output

1
Je pense que la dernière ligne devrait être var_dump($neededObject);:)
Sliq

3

J'aime parfois utiliser la fonction array_reduce () pour effectuer la recherche. C'est similaire à array_filter () mais n'affecte pas le tableau recherché, vous permettant d'effectuer plusieurs recherches sur le même tableau d'objets.

$haystack = array($obj1, $obj2, ...); //some array of objects
$needle = 'looking for me?'; //the value of the object's property we want to find

//carry out the search
$search_results_array = array_reduce(
  $haystack,

  function($result_array, $current_item) use ($needle){
      //Found the an object that meets criteria? Add it to the the result array 
      if ($current_item->someProperty == $needle){
          $result_array[] = $current_item;
      }
      return $result_array;
  },
  array() //initially the array is empty (i.e.: item not found)
);

//report whether objects found
if (count($search_results_array) > 0){
  echo "found object(s): ";
  print_r($search_results_array[0]); //sample object found
} else {
  echo "did not find object(s): ";
}

1
Vous avez une faute de frappe dans votre conditionnel où vous ajoutez au tableau de résultats. Ça devrait être ça:if ($current_item->someProperty == $needle){ $result_array[] = $current_item; }
adrum

Ajusté. Merci @adrum!
yuvilio

1

Je l'ai fait avec une sorte de keymap Java. Si vous faites cela, vous n'avez pas besoin de faire une boucle sur votre tableau d'objets à chaque fois.

<?php

//This is your array with objects
$object1 = (object) array('id'=>123,'name'=>'Henk','age'=>65);
$object2 = (object) array('id'=>273,'name'=>'Koos','age'=>25);
$object3 = (object) array('id'=>685,'name'=>'Bram','age'=>75);
$firstArray = Array($object1,$object2);
var_dump($firstArray);

//create a new array
$secondArray = Array();
//loop over all objects
foreach($firstArray as $value){
    //fill second        key          value
    $secondArray[$value->id] = $value->name;
}

var_dump($secondArray);

echo $secondArray['123'];

production:

array (size=2)
  0 => 
    object(stdClass)[1]
      public 'id' => int 123
      public 'name' => string 'Henk' (length=4)
      public 'age' => int 65
  1 => 
    object(stdClass)[2]
      public 'id' => int 273
      public 'name' => string 'Koos' (length=4)
      public 'age' => int 25
array (size=2)
  123 => string 'Henk' (length=4)
  273 => string 'Koos' (length=4)
Henk

Ah, réindexer le tableau par id! Je fais cela couramment et cela rend les choses plus agréables.
Kzqai

1

Façon d'obtenir instantanément la première valeur:

$neededObject = array_reduce(
    $arrayOfObjects,
    function ($result, $item) use ($searchedValue) {
        return $item->id == $searchedValue ? $item : $result;
    }
);

0

J'ai publié ce que j'utilise pour résoudre ce problème très efficacement ici en utilisant un algorithme de recherche binaire rapide: https://stackoverflow.com/a/52786742/1678210

Je ne voulais pas copier la même réponse. Quelqu'un d'autre l'avait demandé légèrement différemment mais la réponse est la même.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.