Как искать по ключу= > значение в многомерном массиве в PHP
есть ли быстрый способ получить все подмассивы, где пара ключевых значений была найдена в многомерном массиве? Я не могу сказать, насколько глубоким будет массив.
простой пример массива:
$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")
);
Я думаю, что функция должна быть рекурсивной, чтобы спуститься на самый глубокий уровень.
14 ответов:
код:
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); } }
замечательно то, что в основном один и тот же код будет перебирать каталог для вас, используя RecursiveDirectoryIterator вместо RecursiveArrayIterator. СПЛ является "роксор".
единственный облом о 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 строк resultset. Функция занимала вечные 8 секунд, чтобы пройти через все записи, waaaaaay слишком долго.
Мне просто нужна функция, чтобы остановить поиск и вернуться, когда матч был найден. Т. е., если вы ищете customer_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 секундами. все-таки очень дорого но гораздо разумнее.
будьте осторожны с алгоритмами линейного поиска (выше линейны) в многомерных массивах, поскольку они усложняют сложность, поскольку ее глубина увеличивает количество итераций, необходимых для прохождения всего массива. Например:
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)) )
и применить пользовательский интерфейс Comparator и функция для сортировки и поиска их:
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; } } }
спасибо всем, кто разместил здесь.