Búsqueda de matriz multidimensional de PHP (clave de búsqueda por valor específico)

114

Tengo esta matriz multidimensional. Necesito buscarlo y devolver solo la clave que coincida con el valor de "slug". Sé que hay otros hilos sobre la búsqueda de matrices multidimensionales, pero realmente no entiendo lo suficiente como para aplicar a mi situación. ¡Muchas gracias por la ayuda!

Entonces necesito una función como:

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

Aquí está la matriz:

$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
fuente

Respuestas:

157

Muy simple:

function myfunction($products, $field, $value)
{
   foreach($products as $key => $product)
   {
      if ( $product[$field] === $value )
         return $key;
   }
   return false;
}
Aurelio De Rosa
fuente
6
Si está usando esta función en una declaración condicional, querrá hacer una verificación absoluta contra el tipo porque la clave devuelta a veces puede tener un índice de [0]. Entonces, si realiza una verificación condicional, debería verse así: if (myfunction($array, 'field', 'value') !== FALSE )) // do something...
Andy Cook
159

Otra posible solución se basa en la array_search()función. Usted necesita usar PHP 5.5.0 o superior.

Ejemplo

$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

Explicación

La función array_search()tiene dos argumentos. El primero es el valor que desea buscar. El segundo es donde debe buscar la función. La función array_column()obtiene los valores de los elementos cuya clave es 'uid'.

Resumen

Entonces podrías usarlo como:

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

o, si lo prefiere:

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

El ejemplo original (por xfoxawy) se puede encontrar en DOCS .
La array_column() pagina .


Actualizar

Debido al comentario de Vael tuve curiosidad, por lo que hice una prueba simple para medir el desempeño del método que utiliza array_searchy el método propuesto sobre la respuesta aceptada.

Creé una matriz que contenía 1000 matrices, la estructura era así (todos los datos fueron aleatorios):

[
      {
            "_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"
      },...
]

Ejecuté la prueba de búsqueda 100 veces buscando diferentes valores para el campo de nombre, y luego calculé el tiempo medio en milisegundos . Aquí puedes ver un ejemplo.

Los resultados fueron que el método propuesto en esta respuesta necesitaba alrededor de 2E-7 para encontrar el valor, mientras que el método de respuesta aceptado necesitaba alrededor de 8E-7.

Como dije antes, ambas ocasiones son bastante aceptables para una aplicación que usa una matriz con este tamaño. Si el tamaño crece mucho, digamos 1 millón de elementos, esta pequeña diferencia también aumentará.

Actualización II

Agregué una prueba para el método basado en el array_walk_recursiveque se mencionó y en algunas de las respuestas aquí. El resultado obtenido es el correcto. Y si nos centramos en el rendimiento, es un poco peor que los otros examinados en la prueba . En la prueba, puede ver que es aproximadamente 10 veces más lento que el método basado en array_search. Nuevamente, esta no es una diferencia muy relevante para la mayoría de las aplicaciones.

Actualización III

Gracias a @mickmackusa por detectar varias limitaciones en este método:

  • Este método fallará en claves asociativas.
  • Este método solo funcionará en subarreglos indexados (comenzando desde 0 y con claves ascendentes consecutivamente).
Iván Rodríguez Torres
fuente
¿Alguien conoce el desempeño de este? Parece que, en última instancia, sería más lento y aún requeriría 5.5. No puedo probar porque estoy en 5.4.
Vael Victus
Para cualquiera que no entienda: en php 7, los bucles for son más rápidos. Cuando cambié a 5.6 en ese ejemplo de eval.in, array_search fue un poco más rápido.
Vael Victus
¡inteligente! Estaba haciendo algo similar, usando array_combine () con array_column () para hacer otra matriz de la cual tomar mi datum con una clave conocida, pero esto es más elegante.
David
4
Usar array_search()con array_column()no funcionará en la matriz de muestra del OP porque las claves de la submatriz comienzan desde 1. Este método también fallará en claves asociativas. Este método solo funcionará en subarreglos indexados (comenzando desde 0y con claves ascendentes consecutivas). La razón de esto es porque array_column()generará nuevos índices en su matriz devuelta.
mickmackusa
Totalmente correcto @mickmackusa, agregué tu conocimiento a la respuesta. Gracias por la ayuda
Iván Rodríguez Torres
14

Este método de clase puede buscar en una matriz por múltiples condiciones:

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

Producirá:

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
fuente
Hola, Fatalist stackoverflow.com/questions/40860030/… . Está relacionado con estas preguntas, ¿puede aclarar esa pregunta
KARTHI SRV
4

Utilice esta función:

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

y función de llamada.

print_r(searchThroughArray('breville-one-touch-tea-maker-BTM800XL',$products));
josef
fuente
Buena respuesta. Puedes comprobar el desempeño de tu propuesta en mi respuesta
Iván Rodríguez Torres
Las respuestas de solo código tienen un valor bajo en StackOverflow. Actualice su publicación para explicar cómo funciona su función de búsqueda de subcadenas de nodo hoja. Este método no está diseñado específicamente para funcionar como pide el OP, por lo que es importante aclarar las diferencias. Un enlace de demostración mejoraría enormemente la comprensión del lector. Siempre publique respuestas con la intención de educar al OP y a la mayor audiencia 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
fuente
Las respuestas de solo código tienen un valor bajo en StackOverflow. Actualice su publicación para explicar cómo funciona su método recursivo, las situaciones en las que es apropiado y las situaciones en las que la recursión es una sobrecarga innecesaria. Siempre publique respuestas con la intención de educar al OP y a la mayor audiencia de SO.
mickmackusa
1

Para el próximo visitante que venga: use la matriz recursiva walk; visita cada "hoja" en la matriz multidimensional. Aquí tienes inspiración:

function getMDArrayValueByKey($a, $k) {
    $r = [];
    array_walk_recursive ($a, 
                          function ($item, $key) use ($k, &$r) {if ($key == $k) $r[] = $item;}
                          );
    return $r;
}
Hans
fuente
¡No hay problema! solo para ahorrarle tiempo, si prueba josef answer, la función devuelve una matriz con un elemento. La clave es la respuesta deseada :)
Iván Rodríguez Torres
La respuesta de @Ivan josef es muy diferente a esta. ¿Probaste esto tú mismo? Sigo mirando esta respuesta y no creo que pueda funcionar porque array_walk_recursive no puede ver un nivel. Para cada clave de primer nivel, josef llama strpos o verifica todos los nodos de hoja. ¿Ver la diferencia?
mickmackusa
Por supuesto @mickmackusa Pero Hans está dando algún tipo de inspiración, la respuesta no es dar la solución literalmente. Necesita más elaboración, como hizo Josef en su respuesta. Pero, tiene razón en el punto en que esta respuesta no aborda completamente el problema.
Iván Rodríguez Torres
1

Me gustaría a continuación, dónde $productsestá la matriz real dada en el problema al principio.

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

Prueba esto

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
fuente
Las respuestas de solo código tienen un valor bajo en StackOverflow. Actualice su publicación para explicar cómo funciona su método recursivo, las situaciones en las que es apropiado y las situaciones en las que la recursión es una sobrecarga innecesaria. Siempre publique respuestas con la intención de educar al OP y a la mayor audiencia de SO. PD: creo que la mayoría de los desarrolladores de php preferirán &&y en ||lugar de ANDy ORen su condición. No hay razón para declarar current_key. La comparación $needledebe ser estricta.
mickmackusa