Поиск многомерного массива PHP (поиск ключа по определенному значению)

116

У меня есть этот многомерный массив. Мне нужно найти его и вернуть только тот ключ, который соответствует значению «slug». Я знаю, что есть и другие темы для поиска в многомерных массивах, но я недостаточно понимаю, чтобы применить его к моей ситуации. Большое спасибо за любую помощь!

Поэтому мне нужна такая функция, как:

myfunction($products,'breville-one-touch-tea-maker-BTM800XL');
// returns 1

Вот массив:

$products = array (
1  => array(
        'name'          => 'The Breville One-Touch Tea Maker',
        'slug'          => 'breville-one-touch-tea-maker-BTM800XL',
        'shortname'     => 'The One-Touch Tea Maker',
        'listprice'     => '299.99',
        'price'         => '249.99',
        'rating'        => '9.5',
        'reviews'       => '81',
        'buyurl'        => 'http://www.amazon.com/The-Breville-One-Touch-Tea-Maker/dp/B003LNOPSG',
        'videoref1'     => 'xNb-FOTJY1c',
        'videoref2'     => 'WAyk-O2B6F8',
        'image'         => '812BpgHhjBML.jpg',
        'related1'      => '2',
        'related2'      => '3',
        'related3'      => '4',
        'bestbuy'       => '1',
        'quote'         => '',
        'quoteautor'    => 'K. Martino',
        ),

2  => array(
        'name'          => 'Breville Variable-Temperature Kettle BKE820XL',
        'slug'          => 'breville-variable-temperature-kettle-BKE820XL',
        'shortname'     => 'Variable Temperature Kettle',
        'listprice'     => '199.99',
        'price'         => '129.99',
        'rating'        => '9',
        'reviews'       => '78',
        'buyurl'        => 'http://www.amazon.com/Breville-BKE820XL-Variable-Temperature-1-8-Liter-Kettle/dp/B001DYERBK',
        'videoref1'     => 'oyZWBD83xeE',
        'image'         => '41y2B8jSKmwL.jpg',
        'related1'      => '3',
        'related2'      => '4',
        'related3'      => '5',
        'bestbuy'       => '1',
        'quote'         => '',
        'quoteautor'    => '',
        ),
);
Бен Коуба
источник

Ответы:

157

Очень просто:

function myfunction($products, $field, $value)
{
   foreach($products as $key => $product)
   {
      if ( $product[$field] === $value )
         return $key;
   }
   return false;
}
Аурелио де Роса
источник
6
Если вы используете эту функцию в условном выражении, вам нужно будет выполнить абсолютную проверку на соответствие типу, потому что возвращаемый ключ иногда может иметь индекс [0]. Итак, если вы выполняете условную проверку, она должна выглядеть примерно так: if (myfunction($array, 'field', 'value') !== FALSE )) // do something...
Энди Кук
160

Другое возможное решение основано на array_search()функции. Вам необходимо использовать PHP 5.5.0 или выше.

пример

$userdb=Array
(
(0) => Array
    (
        (uid) => '100',
        (name) => 'Sandra Shush',
        (url) => 'urlof100'
    ),

(1) => Array
    (
        (uid) => '5465',
        (name) => 'Stefanie Mcmohn',
        (pic_square) => 'urlof100'
    ),

(2) => Array
    (
        (uid) => '40489',
        (name) => 'Michael',
        (pic_square) => 'urlof40489'
    )
);

$key = array_search(40489, array_column($userdb, 'uid'));

echo ("The key is: ".$key);
//This will output- The key is: 2

объяснение

У функции array_search()два аргумента. Первое - это значение, которое вы хотите найти. Во-вторых, функция должна искать. Функция array_column()получает значения элементов, у которых есть ключ 'uid'.

Резюме

Таким образом, вы можете использовать его как:

array_search('breville-one-touch-tea-maker-BTM800XL', array_column($products, 'slug'));

или, если хотите:

// define function
function array_search_multidim($array, $column, $key){
    return (array_search($key, array_column($array, $column)));
}

// use it
array_search_multidim($products, 'slug', 'breville-one-touch-tea-maker-BTM800XL');

Исходный пример (от xfoxawy) можно найти в DOCS . Страница .
array_column()


Обновить

Из-за комментария Ваэля мне было любопытно, поэтому я провел простой тест, чтобы проверить производительность метода, который использует, array_searchи метода, предложенного в принятом ответе.

Я создал массив, содержащий 1000 массивов, структура была такой (все данные были рандомизированы):

[
      {
            "_id": "57fe684fb22a07039b3f196c",
            "index": 0,
            "guid": "98dd3515-3f1e-4b89-8bb9-103b0d67e613",
            "isActive": true,
            "balance": "$2,372.04",
            "picture": "http://placehold.it/32x32",
            "age": 21,
            "eyeColor": "blue",
            "name": "Green",
            "company": "MIXERS"
      },...
]

Я запускал поисковый тест 100 раз, ища разные значения для поля имени, а затем вычислял среднее время в миллисекундах . Здесь вы можете увидеть пример.

Результаты показали, что для метода, предложенного в этом ответе, требуется около 2E-7, чтобы найти значение, в то время как для принятого метода ответа требуется около 8E-7.

Как я уже сказал, оба раза вполне допустимы для приложения, использующего массив такого размера. Если размер сильно вырастет, скажем, 1M элементов, то эта небольшая разница также будет увеличена.

Обновление II

Я добавил тест для метода, в array_walk_recursiveкотором упоминалось в некоторых ответах здесь. Полученный результат правильный. А если акцентировать внимание на производительности, то она немного хуже, чем у других рассмотренных на тесте . В тесте видно, что это примерно в 10 раз медленнее, чем метод на основе array_search. Опять же, это различие не очень актуально для большинства приложений.

Обновление III

Спасибо @mickmackusa за обнаружение нескольких ограничений этого метода:

  • Этот метод не работает с ассоциативными ключами.
  • Этот метод будет работать только с индексированными подмассивами (начиная с 0 и имеющими последовательно возрастающие ключи).
Иван Родригес Торрес
источник
Кто-нибудь знает производительность этого? Похоже, что в конечном итоге это будет медленнее, и все равно потребуется 5.5. Я не могу тестировать, так как у меня 5.4.
Vael Victus
Для тех, кто не понимает: в php 7 циклы for работают быстрее. Когда я перешел на 5.6 в этом примере eval.in, array_search был немного быстрее.
Vael Victus
умная! Я делал что-то подобное, используя array_combine () с array_column (), чтобы создать еще один массив, из которого можно было бы получить мои данные с известным ключом, но это более элегантно.
Дэвид
4
Использование array_search()with array_column()не будет работать с образцом массива OP, потому что ключи подмассива начинаются с 1. Этот метод также не работает с ассоциативными ключами. Этот метод будет работать только с индексированными подмассивами (начиная с 0ключей и имеющих последовательно возрастающие ключи). Причина этого в том, что array_column()в возвращаемом массиве будут генерироваться новые индексы.
mickmackusa
Совершенно верно @mickmackusa, я добавил ваши знания к ответу. Спасибо за помощь
Иван Родригес Торрес
14

Этот метод класса может выполнять поиск в массиве по нескольким условиям:

class Stdlib_Array
{
    public static function multiSearch(array $array, array $pairs)
    {
        $found = array();
        foreach ($array as $aKey => $aVal) {
            $coincidences = 0;
            foreach ($pairs as $pKey => $pVal) {
                if (array_key_exists($pKey, $aVal) && $aVal[$pKey] == $pVal) {
                    $coincidences++;
                }
            }
            if ($coincidences == count($pairs)) {
                $found[$aKey] = $aVal;
            }
        }

        return $found;
    }    
}

// Example:

$data = array(
    array('foo' => 'test4', 'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test1', 'bar' => 'baz3'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz4'),
    array('foo' => 'test4', 'bar' => 'baz1'),
    array('foo' => 'test',  'bar' => 'baz1'),
    array('foo' => 'test3', 'bar' => 'baz2'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test',  'bar' => 'baz'),
    array('foo' => 'test4', 'bar' => 'baz1')
);

$result = Stdlib_Array::multiSearch($data, array('foo' => 'test4', 'bar' => 'baz1'));

var_dump($result);

Изготовим:

array(2) {
  [5]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
  [10]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
}
фаталист
источник
Привет, фаталист stackoverflow.com/questions/40860030/… . Он относится к этому вопросу, не могли бы вы прояснить этот вопрос
KARTHI SRV
4

Используйте эту функцию:

function searchThroughArray($search,array $lists){
        try{
            foreach ($lists as $key => $value) {
                if(is_array($value)){
                    array_walk_recursive($value, function($v, $k) use($search ,$key,$value,&$val){
                        if(strpos($v, $search) !== false )  $val[$key]=$value;
                    });
            }else{
                    if(strpos($value, $search) !== false )  $val[$key]=$value;
                }

            }
            return $val;

        }catch (Exception $e) {
            return false;
        }
    }

и вызовите функцию.

print_r(searchThroughArray('breville-one-touch-tea-maker-BTM800XL',$products));
йозеф
источник
Хороший ответ. Вы можете проверить эффективность своего предложения по моему ответу
Иван Родригес Торрес
Ответы только на код не имеют большого значения в StackOverflow. Пожалуйста, обновите свой пост, чтобы объяснить, как работает функция поиска подстроки листовых узлов. Этот метод не предназначен специально для работы в соответствии с запросом OP, поэтому важно уточнить различия. Демонстрационная ссылка значительно улучшит понимание читателем. Всегда публикуйте ответы с намерением обучить ОП и большую аудиторию SO.
mickmackusa
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; 
} 
микели
источник
Ответы только на код не имеют большого значения в StackOverflow. Пожалуйста, обновите свой пост, чтобы объяснить, как работает ваш рекурсивный метод, в каких ситуациях он уместен и в каких случаях рекурсия не требует дополнительных затрат. Всегда публикуйте ответы с намерением обучить ОП и большую аудиторию SO.
mickmackusa
1

Для следующего посетителя: используйте рекурсивный обход массива; он посещает каждый «лист» многомерного массива. Вот для вдохновения:

function getMDArrayValueByKey($a, $k) {
    $r = [];
    array_walk_recursive ($a, 
                          function ($item, $key) use ($k, &$r) {if ($key == $k) $r[] = $item;}
                          );
    return $r;
}
Hans
источник
Нет проблем! просто чтобы сэкономить ваше время, если вы попробуете ответить josef, функция вернет массив с одним элементом. Ключ - желаемый ответ :)
Иван Родригес Торрес
Ответ @Ivan josef сильно отличается от этого. Вы сами это проверяли. Я слежу за этим ответом и не думаю, что он может сработать, потому что array_walk_recursive не может видеть уровень выше. Для каждого ключа первого уровня josef вызывает strpos или проверяет все листовые узлы. Увидеть разницу?
mickmackusa
Конечно, @mickmackusa. Но Ганс дает какое-то вдохновение, ответ не дает буквально решения. Это требует доработки, как это сделал Йозеф в своем ответе. Но вы правы в том, что этот ответ не решает проблему полностью.
Иван Родригес Торрес
1

Я бы хотел, чтобы ниже $productsбыл фактический массив, указанный в задаче в самом начале.

print_r(
  array_search("breville-variable-temperature-kettle-BKE820XL", 
  array_map(function($product){return $product["slug"];},$products))
);
Сэм Каз
источник
0

Попробуй это

function recursive_array_search($needle,$haystack) {
        foreach($haystack as $key=>$value) {
            $current_key=$key;
            if($needle==$value['uid'] OR (is_array($value) && recursive_array_search($needle,$value) !== false)) {
                return $current_key;
            }
        }
        return false;
    }
Паван Сен
источник
Ответы только на код не имеют большого значения в StackOverflow. Пожалуйста, обновите свой пост, чтобы объяснить, как работает ваш рекурсивный метод, в каких ситуациях он уместен и в каких случаях рекурсия не требует дополнительных затрат. Всегда публикуйте ответы с намерением обучить ОП и большую аудиторию SO. Ps думаю большинство php разработчиков предпочтут &&и ||вместо ANDи ORв вашем состоянии. Нет причин заявлять current_key. Сравнение $needleдолжно быть строгим.
mickmackusa