Как искать по ключу=>значение в многомерном массиве в 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, вам нужно указать часть pass-by-reference в вызов to 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, мы знаем у нас есть только один в resultset, и как только мы найдем 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) (общее количество элементов порядка во всем array), это плохо, миллион записей (например, массив 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;
}