Есть ли какой-либо быстрый способ получить все подмассивы, где пара ключевых значений была найдена в многомерном массиве? Я не могу сказать, насколько глубоким будет массив.
Простой пример массива:
$arr = array(0 => array(id=>1,name=>"cat 1"), 1 => array(id=>2,name=>"cat 2"), 2 => array(id=>3,name=>"cat 1") );
Когда я ищу ключ = имя и значение = "cat 1", функция должна возвращать:
array(0 => array(id=>1,name=>"cat 1"), 1 => array(id=>3,name=>"cat 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; } $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'));
Вывод:
Array ( [0] => Array ( [id] => 1 [name] => cat 1 ) [1] => Array ( [id] => 3 [name] => cat 1 ) )
Если эффективность важна, вы можете написать ее так, чтобы все рекурсивные вызовы сохраняли свои результаты в одном и том же временном массиве $results
а не объединяли массивы вместе:
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); } }
Ключ в том, что search_r
принимает свой четвертый параметр по ссылке, а не по значению; амперсанд &
имеет решающее значение.
FYI: Если у вас установлена более старая версия PHP, вам нужно указать часть прохода по ссылке в search_r
а не в ее объявлении. То есть, последняя строка становится search_r($subarray, $key, $value, &$results)
.
Как насчет версии SPL ? Это спасет вас от ввода:
// 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); } }
Замечательно то, что в основном один и тот же код будет перебирать через каталог для вас, используя рекурсивныйDirectoryIterator вместо рекурсивногоArrayIterator. SPL – это роксор.
Единственным обломком SPL является то, что он плохо документирован в Интернете. Но несколько книг PHP посвящены некоторым полезным деталям, особенно Pro PHP; и вы можете, вероятно, google и получить дополнительную информацию.
<?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); ?>
Вернулся, чтобы опубликовать это обновление для тех, кто нуждается в совете по оптимизации этих ответов, в частности, отличный ответ Джона Кугельмана выше.
Его опубликованная функция работает нормально, но мне пришлось оптимизировать этот сценарий для обработки набора результатов в 12 000 строк. Функция выполняла вечные 8 секунд, чтобы пройти через все записи, waaaaaay слишком долго.
Я просто нуждался в функции STOP для поиска и возврата, когда совпадение найдено. То есть, если вы ищете client_id, мы знаем, что у нас есть только один в наборе результатов, и как только мы найдем customer_id в многомерном массиве, мы хотим вернуться.
Вот оптимизированная по скорости (и значительно упрощенная) версия этой функции для всех, кто в ней нуждается. В отличие от другой версии, она может обрабатывать только одну глубину массива, не рекурсивно и не позволяет слить несколько результатов.
// 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; } }
Это сбило задачу, чтобы соответствовать 12 000 записей за 1,5 секунды. Все еще очень дорого, но гораздо более разумно.
if (isset($array[$key]) && $array[$key] == $value)
Небольшое преимущество быстрой версии.
Будьте осторожны с алгоритмами линейного поиска (приведенные выше линейными) в многомерных массивах, поскольку они усложняют сложность, так как ее глубина увеличивает количество итераций, необходимых для прохождения по всему массиву. Например:
array( [0] => array ([0] => something, [1] => something_else)) ... [100] => array ([0] => something100, [1] => something_else100)) )
потребовалось бы всего 200 итераций, чтобы найти то, что вы ищете (если игла была в [100] [1]), с подходящим алгоритмом.
Линейные алгоритмы в этом случае выполняются в O (n) (общее количество элементов в целом массиве), это плохо, миллион записей (например, массив 1000x100x10) потребует в среднем 500 000 итераций, чтобы найти иглу. Также, что произойдет, если вы решите изменить структуру вашего многомерного массива? И PHP выработал бы рекурсивный алгоритм, если бы ваша глубина была больше 100. Информатика может сделать лучше:
По возможности всегда используйте объекты вместо многомерных массивов:
ArrayObject( MyObject(something, something_else)) ... MyObject(something100, something_else100)) )
и примените пользовательский интерфейс и функцию компаратора для их сортировки и поиска:
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); }
Вы можете использовать uasort()
для использования пользовательского компаратора, если вы чувствуете себя авантюристом, вы должны реализовать свои собственные коллекции для своих объектов, которые могут их сортировать и управлять ими (я всегда расширяю ArrayObject, чтобы включить функцию поиска, по крайней мере).
$arrayObj->uasort("myComp");
Когда они сортируются (uasort – это O (n log n), что так же хорошо, как и по произвольным данным), двоичный поиск может выполнять операцию в O (log n), т. Е. Миллион записей занимает всего 20 итераций поиск. Насколько мне известно, бинарный поиск не выполняется в PHP ( array_search()
использует естественное упорядочение, которое работает с объектными ссылками, а не их свойствами), вы должны реализовать это свое я, как я.
Этот подход более эффективен (больше нет глубины) и, что более важно, универсален (при условии, что вы обеспечиваете сопоставимость с использованием интерфейсов), поскольку объекты определяют, как они сортируются, поэтому вы можете бесконечно перерабатывать код. Гораздо лучше =)
$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; });
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));
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; }
Мне нужно было что-то подобное, но искать многомерный массив по значению … Я взял пример Джона и написал
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; }
Надеюсь, это поможет кому-то 🙂
Это пересмотренная функция от той, которую написал Джон К. … Мне нужно захватить только конкретный ключ в массиве и ничего выше него.
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'));
Вот решение:
<?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; ?>
И еще одна версия, которая возвращает значение ключа из элемента массива, в котором найденное значение (без рекурсии, оптимизировано для скорости):
// 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; } } }
Спасибо всем, кто разместил здесь.
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; }