Pesquisa de array multidimensional de PHP (encontrar chave por valor específico)

114

Eu tenho esta matriz multidimensional. Preciso pesquisar e retornar apenas a chave que corresponda ao valor do "slug". Eu sei que existem outros tópicos sobre a busca de matrizes multidimensionais, mas não estou realmente entendendo o suficiente para aplicar à minha situação. Muito obrigado por qualquer ajuda!

Portanto, preciso de uma função como:

myfunction($products,'breville-one-touch-tea-maker-BTM800XL');
// returns 1

Aqui está o Array:

$products = array (
1  => array(
        'name'          => 'The Breville One-Touch Tea Maker',
        'slug'          => 'breville-one-touch-tea-maker-BTM800XL',
        'shortname'     => 'The One-Touch Tea Maker',
        'listprice'     => '299.99',
        'price'         => '249.99',
        'rating'        => '9.5',
        'reviews'       => '81',
        'buyurl'        => 'http://www.amazon.com/The-Breville-One-Touch-Tea-Maker/dp/B003LNOPSG',
        'videoref1'     => 'xNb-FOTJY1c',
        'videoref2'     => 'WAyk-O2B6F8',
        'image'         => '812BpgHhjBML.jpg',
        'related1'      => '2',
        'related2'      => '3',
        'related3'      => '4',
        'bestbuy'       => '1',
        'quote'         => '',
        'quoteautor'    => 'K. Martino',
        ),

2  => array(
        'name'          => 'Breville Variable-Temperature Kettle BKE820XL',
        'slug'          => 'breville-variable-temperature-kettle-BKE820XL',
        'shortname'     => 'Variable Temperature Kettle',
        'listprice'     => '199.99',
        'price'         => '129.99',
        'rating'        => '9',
        'reviews'       => '78',
        'buyurl'        => 'http://www.amazon.com/Breville-BKE820XL-Variable-Temperature-1-8-Liter-Kettle/dp/B001DYERBK',
        'videoref1'     => 'oyZWBD83xeE',
        'image'         => '41y2B8jSKmwL.jpg',
        'related1'      => '3',
        'related2'      => '4',
        'related3'      => '5',
        'bestbuy'       => '1',
        'quote'         => '',
        'quoteautor'    => '',
        ),
);
Ben Kouba
fonte

Respostas:

157

Muito simples:

function myfunction($products, $field, $value)
{
   foreach($products as $key => $product)
   {
      if ( $product[$field] === $value )
         return $key;
   }
   return false;
}
Aurelio De Rosa
fonte
6
Se você estiver usando esta função em uma instrução condicional, você vai querer fazer uma verificação absoluta em relação ao tipo, porque a chave retornada às vezes pode ter um índice de [0]. Então, se estiver fazendo uma verificação condicional, deve ser algo assim: if (myfunction($array, 'field', 'value') !== FALSE )) // do something...
Andy Cook
159

Outra solução possível é baseada na array_search()função. Você precisa usar o PHP 5.5.0 ou superior.

Exemplo

$userdb=Array
(
(0) => Array
    (
        (uid) => '100',
        (name) => 'Sandra Shush',
        (url) => 'urlof100'
    ),

(1) => Array
    (
        (uid) => '5465',
        (name) => 'Stefanie Mcmohn',
        (pic_square) => 'urlof100'
    ),

(2) => Array
    (
        (uid) => '40489',
        (name) => 'Michael',
        (pic_square) => 'urlof40489'
    )
);

$key = array_search(40489, array_column($userdb, 'uid'));

echo ("The key is: ".$key);
//This will output- The key is: 2

Explicação

A função array_search()possui dois argumentos. O primeiro é o valor que você deseja pesquisar. O segundo é onde a função deve pesquisar. A função array_column()obtém os valores dos elementos cuja chave é 'uid'.

Resumo

Então você pode usá-lo como:

array_search('breville-one-touch-tea-maker-BTM800XL', array_column($products, 'slug'));

ou, se preferir:

// define function
function array_search_multidim($array, $column, $key){
    return (array_search($key, array_column($array, $column)));
}

// use it
array_search_multidim($products, 'slug', 'breville-one-touch-tea-maker-BTM800XL');

O exemplo original (por xfoxawy) pode ser encontrado no DOCS .
A array_column() página .


Atualizar

Devido ao comentário do Vael fiquei curioso, então fiz um teste simples para medir o desempenho do método que utiliza array_searche do método proposto na resposta aceita.

Eu criei um array que continha 1000 arrays, a estrutura era assim (todos os dados eram randomizados):

[
      {
            "_id": "57fe684fb22a07039b3f196c",
            "index": 0,
            "guid": "98dd3515-3f1e-4b89-8bb9-103b0d67e613",
            "isActive": true,
            "balance": "$2,372.04",
            "picture": "http://placehold.it/32x32",
            "age": 21,
            "eyeColor": "blue",
            "name": "Green",
            "company": "MIXERS"
      },...
]

Executei o teste de pesquisa 100 vezes procurando por valores diferentes para o campo de nome e, em seguida, calculei o tempo médio em milissegundos . Aqui você pode ver um exemplo.

Os resultados foram que o método proposto nesta resposta precisava de cerca de 2E-7 para encontrar o valor, enquanto o método de resposta aceito precisava de cerca de 8E-7.

Como eu disse antes, ambas as vezes são bastante aceitáveis ​​para um aplicativo que usa um array com este tamanho. Se o tamanho aumentar muito, digamos 1M de elementos, essa pequena diferença também aumentará.

Atualização II

Eu adicionei um teste para o método baseado no array_walk_recursivequal foi mencionado em algumas das respostas aqui. O resultado obtido é o correto. E se nos concentrarmos no desempenho, é um pouco pior do que os outros examinados no teste . No teste, você pode ver que é cerca de 10 vezes mais lento do que o método baseado em array_search. Novamente, essa não é uma diferença muito relevante para a maioria dos aplicativos.

Atualização III

Agradecimentos a @mickmackusa por detectar várias limitações neste método:

  • Este método falhará em chaves associativas.
  • Este método só funcionará em subarrays indexados (começando em 0 e têm chaves ascendentes consecutivas).
Iván Rodríguez Torres
fonte
Alguém sabe o desempenho disso? Parece que no final das contas seria mais lento e ainda exigiria 5.5. Não consigo testar porque estou no 5.4.
Vael Victus
Para quem não entende: no php 7, os loops for são mais rápidos. Quando mudei para 5.6 nesse exemplo eval.in, array_search foi um pouco mais rápido.
Vael Victus
inteligente! Eu estava fazendo algo parecido, usando array_combine () com array_column () para fazer outro array do qual pegar meu datum com uma chave conhecida, mas isso é mais elegante.
David,
4
Usar array_search()com array_column()não funcionará no array de amostra do OP porque as chaves do subarray começam em 1. Este método também falhará em chaves associativas. Este método só funcionará em submatrizes indexados (começando 0e tendo chaves ascendentes consecutivas). A razão para isso é porque array_column()irá gerar novos índices em sua matriz retornada.
mickmackusa
totalmente certo @mickmackusa, acrescentei seu conhecimento à resposta. Obrigado pela ajuda
Iván Rodríguez Torres
14

Este método de classe pode pesquisar na matriz por várias condições:

class Stdlib_Array
{
    public static function multiSearch(array $array, array $pairs)
    {
        $found = array();
        foreach ($array as $aKey => $aVal) {
            $coincidences = 0;
            foreach ($pairs as $pKey => $pVal) {
                if (array_key_exists($pKey, $aVal) && $aVal[$pKey] == $pVal) {
                    $coincidences++;
                }
            }
            if ($coincidences == count($pairs)) {
                $found[$aKey] = $aVal;
            }
        }

        return $found;
    }    
}

// Example:

$data = array(
    array('foo' => 'test4', 'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test1', 'bar' => 'baz3'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz4'),
    array('foo' => 'test4', 'bar' => 'baz1'),
    array('foo' => 'test',  'bar' => 'baz1'),
    array('foo' => 'test3', 'bar' => 'baz2'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test4', 'bar' => 'baz1')
);

$result = Stdlib_Array::multiSearch($data, array('foo' => 'test4', 'bar' => 'baz1'));

var_dump($result);

Vai produzir:

array(2) {
  [5]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
  [10]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
}
Fatalista
fonte
Olá Fatalist, stackoverflow.com/questions/40860030/… . É reletado a estas questões, por favor, esclareçam essa questão
KARTHI SRV
4

Use esta função:

function searchThroughArray($search,array $lists){
        try{
            foreach ($lists as $key => $value) {
                if(is_array($value)){
                    array_walk_recursive($value, function($v, $k) use($search ,$key,$value,&$val){
                        if(strpos($v, $search) !== false )  $val[$key]=$value;
                    });
            }else{
                    if(strpos($value, $search) !== false )  $val[$key]=$value;
                }

            }
            return $val;

        }catch (Exception $e) {
            return false;
        }
    }

e função de chamada.

print_r(searchThroughArray('breville-one-touch-tea-maker-BTM800XL',$products));
Josef
fonte
Boa resposta. Você pode conferir o desempenho de sua proposta em minha resposta
Iván Rodríguez Torres
Respostas somente de código são de baixo valor em StackOverflow. Atualize sua postagem para explicar como funciona a função de pesquisa de substring de nó-folha. Este método não foi projetado especificamente para funcionar como o OP está pedindo, portanto, é importante esclarecer as diferenças. Um link de demonstração melhoraria muito a compreensão do leitor. Sempre poste respostas com a intenção de educar o OP e o grande público de SO.
mickmackusa
1
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; 
} 
Mikelee
fonte
Respostas somente de código são de baixo valor em StackOverflow. Atualize sua postagem para explicar como seu método recursivo funciona, as situações em que é apropriado e as situações em que a recursão é uma sobrecarga desnecessária. Sempre poste respostas com a intenção de educar o OP e o grande público de SO.
mickmackusa
1

Para o próximo visitante que chegar: use a caminhada de array recursiva; ele visita cada "folha" no array multidimensional. Aqui está a inspiração:

function getMDArrayValueByKey($a, $k) {
    $r = [];
    array_walk_recursive ($a, 
                          function ($item, $key) use ($k, &$r) {if ($key == $k) $r[] = $item;}
                          );
    return $r;
}
Hans
fonte
Sem problemas! só para economizar seu tempo, se você tentar josef answer, a função retorna um array com um elemento. A chave é a resposta desejada :)
Iván Rodríguez Torres
A resposta de @Ivan Josef é muito diferente desta. Você mesmo testou. Continuo pensando nessa resposta e não acho que funcione porque array_walk_recursive não consegue ver um nível acima. Para cada chave de primeiro nível, josef está chamando strpos ou verificando todos os leafnodes. Veja a diferença?
mickmackusa
Claro @mickmackusa. Mas Hans está dando algum tipo de inspiração, a resposta não é dar a solução literalmente. Precisa ser mais elaborado, como Josef fez em sua resposta. Mas você está certo no ponto de que essa resposta não resolve completamente o problema.
Iván Rodríguez Torres
1

Eu gostaria de fazer o seguinte, onde $productsestá a matriz real fornecida no problema no início.

print_r(
  array_search("breville-variable-temperature-kettle-BKE820XL", 
  array_map(function($product){return $product["slug"];},$products))
);
Sam Kaz
fonte
0

Tente isto

function recursive_array_search($needle,$haystack) {
        foreach($haystack as $key=>$value) {
            $current_key=$key;
            if($needle==$value['uid'] OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
                return $current_key;
            }
        }
        return false;
    }
Pawan Sen
fonte
Respostas somente de código são de baixo valor em StackOverflow. Atualize sua postagem para explicar como seu método recursivo funciona, as situações em que é apropriado e as situações em que a recursão é uma sobrecarga desnecessária. Sempre poste respostas com a intenção de educar o OP e o grande público de SO. Ps, acho que a maioria dos desenvolvedores de php prefere &&e em ||vez de ANDe ORna sua condição. Não há razão para declarar current_key. A comparação $needledeve ser estrita.
mickmackusa