Encontrar entrada por propriedade de objeto de uma matriz de objetos

174

A matriz se parece com:

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

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

E eu tenho uma variável inteira chamada $v.

Como eu poderia selecionar uma entrada de matriz que tenha um objeto em que a IDpropriedade tenha o $vvalor?

Alex
fonte

Respostas:

189

Você itera a matriz, pesquisando o registro específico (ok em uma pesquisa única) ou constrói um hashmap usando outra matriz associativa.

Para o primeiro, algo como isto

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

Veja esta pergunta e respostas subseqüentes para obter mais informações sobre este último - Referência ao array PHP por vários índices

Phil
fonte
3
definir $ item como nulo não é necessário.
DAm2K
32
Opa, aí está :) Isso acontece caso o item procurado não esteja na matriz. Como alternativa, você pode usar, isset($item)mas eu prefiro inicializar as variáveis ​​corretamente #
Phil
3
Para aqueles de vocês com os valores-chave definidos para valores usarif($v == $struct["ID"]){...
wbadart
67

YurkamTim está certo. Precisa apenas de uma modificação:

Após a função ($), você precisa de um ponteiro para a variável externa por "use (& $ managedValue)" e, em seguida, pode acessar a variável externa. Além disso, você pode modificá-lo.

$neededObject = array_filter(
    $arrayOfObjects,
    function ($e) use (&$searchedValue) {
        return $e->id == $searchedValue;
    }
);
Daniel Hardt
fonte
2
Você está certo sobre a modificação e é meio que um método interessante, mas eu testei a velocidade em comparação com a iteração no objeto - você mesmo, porque, como @phil apontou, o array_filter também está fazendo isso - e esse método leva cerca de cinco vezes mais. Meu objeto de teste não é grande, então isso pode piorar ainda mais.
Nicolai
9
Isso &não é necessário ao importar $searchedValuepara o escopo de fechamento. O &é usado para criar uma referência que é necessária apenas se $searchedValuetiver sido modificada dentro do fechamento.
Stefan Gehrig
Isso é legal. Eu não sabia que o PHP poderia fazer coisas assim. Eu pensei que usando globalera o único era compartilhar dados em funções! Mas é uma pena se isso é realmente lento. :(
NoOne 23/01
13
TS pediu uma única entrada, esse código retorna uma matriz.
Pavel Vlasov
57
$arr = [
  [
    'ID' => 1
  ]
];

echo array_search(1, array_column($arr, 'ID')); // prints 0 (!== false)
Tim
fonte
3
Não sei por que essa não é a resposta preferida. É porque você está chamando duas funções?
Doz87 27/08
1
Acho que estava muito atrasado para a festa;) Sua escassez e legibilidade sem loops e intervalos tornariam razoável. Mas ainda não o comparamos. Você tem muitas opções em PHP para conseguir o mesmo.
Tim
3
Solução muito elegante. Também funciona com uma matriz de objetos no PHP 7. No PHP 5: array_search ($ object-> id, array_map (função ($ object)) {retorna $ object-> id;}, $ objects)); No PHP 7: array_search ($ object-> id, array_column ($ objects, 'id'));
Mike
3
Esse não é o respondere preferido, porque op solicita uma matriz de objetos e esse respondere manipula apenas matrizes puras.
Dwza 12/05/19
8
isso não está correto. este código alças matriz de objectos matrizes planas / não
Tim
31

Encontrei uma solução mais elegante aqui . Adaptado à pergunta, pode parecer:

$neededObject = array_filter(
    $arrayOfObjects,
    function ($e) use ($searchedValue) {
        return $e->id == $searchedValue;
    }
);
YurkaTim
fonte
16
+1, mas array_filterretorna uma matriz e não para no primeiro valor encontrado.
Carlos Campderrós
4
Não é reconhecer $searchedValuedentro da função. Mas lá fora está.
M. Ahmad Zafar
4
Para iniciantes, esse código não funciona, pois $searchedValueestá fora do escopo de fechamento. Em segundo lugar, como você acha que esses métodos de matriz funcionam? Todos eles loop sobre a matriz internamente
Phil
1
No tempo de múltiplos núcleos, este - em outros ambientes de programação infelizmente - poderiam ser processadas em paralelo, o loop acima não necessariamente
FloydThreepwood
3
Para usar a $searchedValuenecessidade de escreverfunction ($e) use ($searchedValue) {
Vilintritenmert 26/03/19
20

Usar array_column para indexar novamente poupará tempo se você precisar encontrar várias vezes:

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

Então você pode simplesmente $lookup[$id]à vontade.

Museful
fonte
3
Esta foi a resposta mais surpreendente, mesmo se não é o mais intuitivo ...
Thiago Natanael
11
class ArrayUtils
{
    public static function objArraySearch($array, $index, $value)
    {
        foreach($array as $arrayInf) {
            if($arrayInf->{$index} == $value) {
                return $arrayInf;
            }
        }
        return null;
    }
}

Usá-lo da maneira que você queria seria algo como:

ArrayUtils::objArraySearch($array,'ID',$v);
Pablo SG Pacheco
fonte
9

Experimentar

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

exemplo de trabalho aqui

Kamil Kiełczewski
fonte
1
Muito, muito útil! Obrigado mano!
Fernando León
não vai parar no primeiro elemento encontrado, não é?
yaugenka 20/03
7

Corrigindo um pequeno erro do @YurkaTim , sua solução funciona para mim, mas adicionando use:

Para usar $searchedValue, dentro da função, uma solução pode estar use ($searchedValue)após os parâmetros da função function ($e) HERE.

a array_filterfunção retornará apenas $neededObjectse a condição de retorno fortrue

Se $searchedValueé uma sequência ou número inteiro:

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

Se $searchedValuefor o array onde precisamos verificar com uma lista:

$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
Jose Carlos Ramos Carmenates
fonte
1
Acho última linha deve ser var_dump($neededObject);:)
SliQ
3

Às vezes gosto de usar a função array_reduce () para realizar a pesquisa. É semelhante ao array_filter (), mas não afeta a matriz pesquisada, permitindo realizar várias pesquisas na mesma matriz de objetos.

$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): ";
}
yuvilio
fonte
1
Você tem um erro de digitação dentro do seu condicional, onde está adicionando ao array results_array. Deve ser o seguinte:if ($current_item->someProperty == $needle){ $result_array[] = $current_item; }
adrum 28/02
Ajustado. Obrigado @adrum!
yuvilio
1

Eu fiz isso com algum tipo de mapa de teclas Java. Se você fizer isso, não precisará repetir a matriz de objetos todas as vezes.

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

resultado:

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
Mart-Jan
fonte
Ah, reindexando a matriz por id! Faço isso normalmente e isso torna as coisas mais agradáveis.
Kzqai
1

Maneira de obter instantaneamente o primeiro valor:

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

Publiquei o que eu uso para resolver esse mesmo problema de forma eficiente aqui, usando um algoritmo de pesquisa binária rápido: https://stackoverflow.com/a/52786742/1678210

Eu não queria copiar a mesma resposta. Alguém havia perguntado um pouco diferente, mas a resposta é a mesma.

Justin Jack
fonte