Как искать по ключу => значение в многомерном массиве в PHP - PullRequest
141 голосов
/ 19 июня 2009

Есть ли какой-нибудь быстрый способ получить все подмассивы, в которых пара ключ-значение была найдена в многомерном массиве? Я не могу сказать, насколько глубоким будет массив.

Простой пример массива:

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);

Когда я ищу ключ = имя и значение = "кошка 1", функция должна вернуть:

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);

Я полагаю, что функция должна быть рекурсивной, чтобы перейти на самый глубокий уровень.

Ответы [ 14 ]

198 голосов
/ 19 июня 2009

Код:

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 принимает свой четвертый параметр по ссылке, а не по значению; амперсанд & имеет решающее значение.

К вашему сведению: если у вас более старая версия PHP, вы должны указать часть передачи по ссылке в вызове до search_r, а не в ее объявлении. То есть последняя строка становится search_r($subarray, $key, $value, &$results).

69 голосов
/ 19 июня 2009

Как насчет версии 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 - это роксор.

Единственный облом SPL в том, что он плохо документирован в Интернете. Но несколько книг по PHP посвящены некоторым полезным деталям, в частности, Pro PHP; и вы можете, вероятно, Google для получения дополнительной информации.

42 голосов
/ 20 февраля 2013
<?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);

?>

Ссылка: http://php.net/manual/en/function.array-filter.php

16 голосов
/ 10 июня 2011

Вернулся, чтобы опубликовать это обновление для тех, кому нужна подсказка по оптимизации этих ответов, особенно замечательный ответ Джона Кугельмана выше.

Его опубликованная функция работает нормально, но мне пришлось оптимизировать этот сценарий для обработки 12 000 строк результатов. Функция проходила вечные 8 секунд, чтобы просмотреть все записи, ааааааа, слишком долго.

Мне просто нужна была функция, чтобы ОСТАНОВИТЬ поиск и вернуться, когда совпадение было найдено. То есть, если мы ищем 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 сек. Все еще очень дорого , но гораздо более разумно.

14 голосов
/ 28 июня 2010
if (isset($array[$key]) && $array[$key] == $value)

Незначительное улучшение быстрой версии.

7 голосов
/ 15 июня 2012

Будьте осторожны с алгоритмами линейного поиска (приведенные выше являются линейными) в многомерных массивах, поскольку они имеют сложную сложность, поскольку ее глубина увеличивает количество итераций, необходимых для обхода всего массива. Например:

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() использует естественное упорядочение, которое работает со ссылками на объекты, а не с их свойствами), вам придется реализовать это самостоятельно, как я.

Этот подход более эффективен (больше нет глубины) и, что более важно, универсален (предполагается, что вы применяете сопоставимость с помощью интерфейсов), поскольку объекты определяют способ их сортировки, поэтому вы можете бесконечно перерабатывать код. Гораздо лучше =)

5 голосов
/ 25 января 2012
$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;
});
3 голосов
/ 16 января 2017

Вот решение:

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

?>
3 голосов
/ 23 мая 2011
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;
} 
3 голосов
/ 25 марта 2011

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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...