valor - php array search multidimensional
Cómo buscar por clave=> valor en una matriz multidimensional en PHP (14)
¿Hay alguna manera rápida de obtener todas las submatrices donde se encontró un par de valores clave en una matriz multidimensional? No puedo decir qué tan profunda será la matriz.
Arreglo de ejemplo simple:
$arr = array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>2,name=>"cat 2"),
2 => array(id=>3,name=>"cat 1")
);
Cuando busco key = name y value = "cat 1", la función debería regresar:
array(0 => array(id=>1,name=>"cat 1"),
1 => array(id=>3,name=>"cat 1")
);
Supongo que la función debe ser recursiva para llegar al nivel más profundo.
¿Qué hay de la versión SPL lugar? Te ahorrará algo de tipeo:
// 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);
}
}
Lo bueno es que, básicamente, el mismo código se repetirá a través de un directorio para usted, mediante el uso de un RecursiveDirectoryIterator en lugar de un RecursiveArrayIterator. SPL es el roxor.
El único fastidio de SPL es que está mal documentado en la web. Pero varios libros PHP entran en algunos detalles útiles, particularmente Pro PHP; y probablemente puedas buscar más información en google.
Aquí hay una solución:
<?php
$students[''e1003''][''birthplace''] = ("Mandaluyong <br>");
$students[''ter1003''][''birthplace''] = ("San Juan <br>");
$students[''fgg1003''][''birthplace''] = ("Quezon City <br>");
$students[''bdf1003''][''birthplace''] = ("Manila <br>");
$key = array_search(''Delata Jona'', array_column($students, ''name''));
echo $key;
?>
Código:
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''));
Salida:
Array
(
[0] => Array
(
[id] => 1
[name] => cat 1
)
[1] => Array
(
[id] => 3
[name] => cat 1
)
)
Si la eficiencia es importante, puede escribirla para que todas las llamadas recursivas almacenen sus resultados en la misma matriz temporal $results
lugar de combinar matrices juntas, de esta forma:
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 clave es que search_r
toma su cuarto parámetro por referencia en lugar de por valor; el signo &
es crucial.
FYI: si tiene una versión anterior de PHP, debe especificar la parte de paso por referencia en la llamada a search_r
lugar de en su declaración. Es decir, la última línea se convierte en search_r($subarray, $key, $value, &$results)
.
Esta es una función revisada de la que publicó John K. ... Necesito obtener solo la clave específica de la matriz y nada sobre ella.
function search_array ( $array, $key, $value )
{
$results = array();
if ( is_array($array) )
{
if ( $array[$key] == $value )
{
$results[] = $array;
} else {
foreach ($array as $subarray)
$results = array_merge( $results, $this->search_array($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_array($arr, ''name'', ''cat 1''));
Necesitaba algo similar, pero para buscar matriz multidimensional por valor ... Tomé John ejemplo y escribí
function _search_array_by_value($array, $value) {
$results = array();
if (is_array($array)) {
$found = array_search($value,$array);
if ($found) {
$results[] = $found;
}
foreach ($array as $subarray)
$results = array_merge($results, $this->_search_array_by_value($subarray, $value));
}
return $results;
}
Espero que ayude a alguien :)
Tenga cuidado con los algoritmos de búsqueda lineal (los anteriores son lineales) en matrices de múltiples dimensiones, ya que tienen complejidad compuesta ya que su profundidad aumenta el número de iteraciones necesarias para atravesar toda la matriz. P.ej:
array(
[0] => array ([0] => something, [1] => something_else))
...
[100] => array ([0] => something100, [1] => something_else100))
)
tomaría a lo sumo 200 iteraciones para encontrar lo que está buscando (si la aguja estaba en [100] [1]), con un algoritmo adecuado.
Los algoritmos lineales en este caso funcionan en O (n) (ordena el número total de elementos en la matriz completa), esto es pobre, un millón de entradas (por ejemplo, una matriz de 1000x100x10) tomaría en promedio 500,000 iteraciones para encontrar la aguja. ¿Qué pasaría si decidieras cambiar la estructura de tu matriz multidimensional? Y PHP lanzaría un algoritmo recursivo si tu profundidad fuera más de 100. La informática puede hacerlo mejor:
Siempre que sea posible, siempre use objetos en lugar de matrices multidimensionales:
ArrayObject(
MyObject(something, something_else))
...
MyObject(something100, something_else100))
)
y aplicar una interfaz y función de comparador personalizado para ordenarlas y encontrarlas:
interface Comparable {
public function compareTo(Comparable $o);
}
class MyObject implements Comparable {
public function compareTo(Comparable $o){
...
}
}
function myComp(Comparable $a, Comparable $b){
return $a->compareTo($b);
}
Puede usar uasort()
para utilizar un comparador personalizado. Si se siente aventurero, debe implementar sus propias colecciones para sus objetos que pueden clasificarlos y administrarlos (siempre extiendo ArrayObject para incluir una función de búsqueda como mínimo).
$arrayObj->uasort("myComp");
Una vez que están ordenados (uasort es O (n log n), que es tan bueno como obtiene datos arbitrarios), la búsqueda binaria puede hacer la operación en el tiempo O (log n), es decir, un millón de entradas solo requiere ~ 20 iteraciones para buscar. Por lo que yo sé, la búsqueda binaria del comparador personalizado no está implementada en PHP ( array_search()
usa un ordenamiento natural que funciona en referencias a objetos, no en sus propiedades), tendría que implementarlo como lo hago yo.
Este enfoque es más eficiente (ya no existe una profundidad) y más importante, universal (suponiendo que se exige comparabilidad usando interfaces) ya que los objetos definen cómo se ordenan, por lo que puede reciclar el código infinitamente. Mucho mejor =)
Volvió a publicar esta actualización para cualquiera que necesite una sugerencia de optimización sobre estas respuestas, especialmente la gran respuesta de John Kugelman.
Su función publicada funciona bien pero tuve que optimizar este escenario para manejar un conjunto de resultados de 12,000 filas. La función tardaba 8 segundos eternos en recorrer todos los registros, por mucho tiempo.
Simplemente necesitaba la función parar la búsqueda y regresar cuando se encontró la coincidencia. Es decir, si busca un id_cliente, sabemos que solo tenemos uno en el conjunto de resultados y una vez que encontremos el id_cliente en la matriz multidimensional, queremos regresar.
Aquí está la versión optimizada de velocidad (y mucho más simplificada) de esta función, para cualquier persona que lo necesite. A diferencia de otras versiones, solo puede manejar una profundidad de matriz, no recurre y elimina la fusión de resultados múltiples.
// 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;
}
}
Esto redujo la tarea para unir los 12 000 registros a 1,5 segundos. Aún muy costoso pero mucho más razonable.
Y otra versión que devuelve el valor clave del elemento de matriz en el que se encuentra el valor (sin recursión, optimizado para la velocidad):
// if the array is
$arr[''apples''] = array(''id'' => 1);
$arr[''oranges''] = array(''id'' => 2);
//then
print_r(search_array($arr, ''id'', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) )
// instead of Array ( [0] => Array ( [id] => 2 ) )
// search array for specific key = value
function search_array($array, $key, $value) {
$return = array();
foreach ($array as $k=>$subarray){
if (isset($subarray[$key]) && $subarray[$key] == $value) {
$return[$k] = $subarray;
return $return;
}
}
}
Gracias a todos los que publicaron aquí.
http://snipplr.com/view/51108/nested-array-search-by-value-or-key/
<?php
//PHP 5.3
function searchNestedArray(array $array, $search, $mode = ''value'') {
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
if ($search === ${${"mode"}})
return true;
}
return false;
}
$data = array(
array(''abc'', ''ddd''),
''ccc'',
''bbb'',
array(''aaa'', array(''yyy'', ''mp'' => 555))
);
var_dump(searchNestedArray($data, 555));
$result = array_filter($arr, function ($var) {
$found = false;
array_walk_recursive($var, function ($item, $key) use (&$found) {
$found = $found || $key == "name" && $item == "cat 1";
});
return $found;
});
<?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);
?>
function findKey($tab, $key){
foreach($tab as $k => $value){
if($k==$key) return $value;
if(is_array($value)){
$find = findKey($value, $key);
if($find) return $find;
}
}
return null;
}
function in_multi_array($needle, $key, $haystack)
{
$in_multi_array = false;
if (in_array($needle, $haystack))
{
$in_multi_array = true;
}else
{
foreach( $haystack as $key1 => $val )
{
if(is_array($val))
{
if($this->in_multi_array($needle, $key, $val))
{
$in_multi_array = true;
break;
}
}
}
}
return $in_multi_array;
}
if (isset($array[$key]) && $array[$key] == $value)
Una implementación menor a la versión rápida.