138

I have this multidimensional array. I need to search it and return only the key that matches the value of the "slug". I know there are other threads about searching multidimensional arrays, but I'm not really understanding enough to apply to my situation. Thanks very much for any help!

So I need a function like:

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

Here's the Array:

$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'    => '',
        ),
);
Ben Kouba
  • 1,383
  • 2
  • 9
  • 4
  • You can do it by the Class that I posted here: https://stackoverflow.com/a/69304668/4276501 – Serj Sep 23 '21 at 17:57

10 Answers10

222

Another poossible solution is based on the array_search() function. You need to use PHP 5.5.0 or higher.

Example

$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

Explanation

The function `array_search()` has two arguments. The first one is the value that you want to search. The second is where the function should search. The function `array_column()` gets the values of the elements which key is `'uid'`.

Summary

So you could use it as:
array_search('breville-one-touch-tea-maker-BTM800XL', array_column($products, 'slug'));

or, if you prefer:

// 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');

The original example(by xfoxawy) can be found on the DOCS.
The array_column() page.


Update

Due to Vael comment I was curious, so I made a simple test to meassure the performance of the method that uses array_search and the method proposed on the accepted answer.

I created an array which contained 1000 arrays, the structure was like this (all data was randomized):

[
      {
            "_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"
      },...
]

I ran the search test 100 times searching for different values for the name field, and then I calculated the mean time in milliseconds. Here you can see an example.

Results were that the method proposed on this answer needed about 2E-7 to find the value, while the accepted answer method needed about 8E-7.

Like I said before both times are pretty aceptable for an application using an array with this size. If the size grows a lot, let's say 1M elements, then this little difference will be increased too.

Update II

I've added a test for the method based in array_walk_recursive which was mentionend on some of the answers here. The result got is the correct one. And if we focus on the performance, its a bit worse than the others examined on the test. In the test, you can see that is about 10 times slower than the method based on array_search. Again, this isn't a very relevant difference for the most of the applications.

Update III

Thanks to @mickmackusa for spotting several limitations on this method:

  • This method will fail on associative keys.
  • This method will only work on indexed subarrays (starting from 0 and have consecutively ascending keys).

Note on Update III

  • not taking performance into account: you can use array_combine with array_keys & array_column to overcome this limitation in a one-liner like:
$product_search_index = 
array_search( 'breville-one-touch-tea-maker-BTM800XL', array_filter( array_combine( array_keys($products), array_column( $products, 'slug' ) ) ) );
Iván Rodríguez Torres
  • 4,293
  • 3
  • 31
  • 47
  • 1
    Does anyone know the performance of this? It seems like it would ultimately be slower, and still would require 5.5. I cannot test as I'm on 5.4. – Vael Victus Sep 22 '16 at 21:42
  • For anyone who doesn't understand: in php 7, the for loops are faster. When I changed to 5.6 in that eval.in example, array_search was slightly faster. – Vael Victus Nov 13 '16 at 15:12
  • clever! I was doing something sort of similar, using array_combine() with array_column() to make another array from which to grab my datum with a known key, but this is more elegant. – David Oct 19 '17 at 19:31
  • 9
    Using `array_search()` with `array_column()` will not work on the OP's sample array because the subarray keys start from `1`. This method will also fail on associative keys. This method will only work on indexed subarrays (starting from `0` and have consecutively ascending keys). The reason for this is because `array_column()` will generate new indexes in its returned array. – mickmackusa Jan 29 '18 at 12:56
  • 1
    totally right @mickmackusa, I've added your knowledge to the answer. Thanks for the help – Iván Rodríguez Torres Jan 29 '18 at 14:40
  • Your sample link is broken pal :) and there is a semicolon to much in your array_search_multidim which will break the page – ii iml0sto1 Nov 01 '19 at 08:12
  • @iiiml0sto1 Thanks.It seems that eval.in has some troubles on their site. I'll try to reupload in the next days – Iván Rodríguez Torres Nov 01 '19 at 12:16
  • This will only work if all the subarrays have the key of the value you are looking for, for ex. if name were missing from one of the subarrays the key returned from `array_search` might be off – Moses Schwartz Sep 02 '20 at 15:43
  • @IvánRodríguezTorres The disclaimer that this doesn't work on associative arrays/arrays not starting from 0 needs to be higher up at the top of this answer. I used this code previously in projects which is now failing and only now just realised why (my own fault) after revisiting this page. There will be plenty of others doing the same that wont read the answer in full to realise your answer does not apply to this question (the array in the question starts from 1). – PeteB Nov 24 '22 at 09:44
  • 1
    Be very careful with this! If you don't use validation === false on the result, but use something like if(!$key) you might end up breaking the page in an infinite loop. That's what I did anyway. :D – Dgloria Aug 04 '23 at 08:07
173

Very simple:

function myfunction($products, $field, $value)
{
   foreach($products as $key => $product)
   {
      if ( $product[$field] === $value )
         return $key;
   }
   return false;
}
mukama
  • 969
  • 2
  • 12
  • 28
Aurelio De Rosa
  • 21,856
  • 8
  • 48
  • 71
  • 7
    If you're using this function in a conditional statement you're going to want to do an absolute check against the type because the returned key can sometimes have an index of [0]. So if doing a conditional check, it should look something like this: `if (myfunction($array, 'field', 'value') !== FALSE )) // do something...` – Andy Cook Nov 17 '15 at 21:11
  • 2
    Simple solution for best performance and understanding of technicians. – LUISAO Aug 07 '21 at 15:46
  • Thanks for this. It's simple and reliable. I've tested this and the , array_search($value, array_column($searchArray, $searchKey)) alternative. I get incorrect results from the array_search()/array_column approach. – CheddarMonkey Sep 17 '21 at 19:56
16

This class method can search in array by multiple conditions:

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

Will produce:

array(2) {
  [5]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
  [10]=>
  array(2) {
    ["foo"]=>
    string(5) "test4"
    ["bar"]=>
    string(4) "baz1"
  }
}
Fatalist
  • 361
  • 3
  • 5
  • Hi Fatalist http://stackoverflow.com/questions/40860030/search-through-a-multi-dimensional-array-with-multiple-values/40860348#40860348 . It is releted to these question can you please clarify that question – KARTHI SRV Nov 29 '16 at 13:19
4

Use this function:

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

and call function.

print_r(searchThroughArray('breville-one-touch-tea-maker-BTM800XL',$products));
Cr1xus
  • 427
  • 3
  • 20
josef
  • 872
  • 9
  • 8
  • Nice answer. You can check the performance of your proposal on my answer – Iván Rodríguez Torres Jan 29 '18 at 08:55
  • Code-only answers are low value on StackOverflow. Please update your post to explain how your leaf-node substring search function works. This method is not designed specifically to work as the OP is asking, so it is important to clarify the differences. A demo link would greatly improve reader comprehension. Always post answers with the intent to educate the OP and the greater SO audience. – mickmackusa Jan 30 '18 at 03:15
2
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; 
} 
mikelee
  • 359
  • 1
  • 5
  • 18
  • Code-only answers are low value on StackOverflow. Please update your post to explain how your recursive method works, the situations where it is appropriate, and situations where recursion is unnecessary overhead. Always post answers with the intent to educate the OP and the greater SO audience. – mickmackusa Jan 30 '18 at 02:59
2

I would do like below, where $products is the actual array given in the problem at the very beginning.

print_r(
  array_search("breville-variable-temperature-kettle-BKE820XL", 
  array_map(function($product){return $product["slug"];},$products))
);
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Sam Kaz
  • 432
  • 3
  • 12
0

Try this

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;
    }
pawan sen
  • 716
  • 5
  • 14
  • 1
    Code-only answers are low value on StackOverflow. Please update your post to explain how your recursive method works, the situations where it is appropriate, and situations where recursion is unnecessary overhead. Always post answers with the intent to educate the OP and the greater SO audience. P.s. I think most php developers will prefer to `&&` and `||` instead of `AND` and `OR` in your condition. There is no reason to declare `current_key`. The comparison on `$needle` should be strict. – mickmackusa Jan 30 '18 at 03:01
0

For the next visitor coming along: use the recursive array walk; it visits every "leaf" in the multidimensional array. Here's for inspiration:

function getMDArrayValueByKey($a, $k) {
    $r = [];
    array_walk_recursive ($a, 
                          function ($item, $key) use ($k, &$r) {if ($key == $k) $r[] = $item;}
                          );
    return $r;
}
Hans
  • 51
  • 4
  • No problem! just to save you time, if you try josef answer, the function returns an array with one element. The key is the desired answer :) – Iván Rodríguez Torres Jan 29 '18 at 09:08
  • @Ivan josef's answer is very different from this one. Did you test this yourself. I keep eyeballing this answer and I don't think it can work because array_walk_recursive can't see up a level. For each first level key, josef is calling strpos or checking all leafnodes. See the difference? – mickmackusa Jan 29 '18 at 09:46
  • Of course @mickmackusa But Hans is giving some kind of inspiration, the answer is not giving the solution literally. It needs more elaboration, as Josef did on his answer. But, you are right in the point that this answer doesn't completely address the problem. – Iván Rodríguez Torres Jan 29 '18 at 10:18
  • Answers that do not correctly resolve the question asked are "incorrect" and are likely to mislead/confuse researchers. – mickmackusa May 02 '21 at 03:39
0

You can convert the array to JSON and search as a string then return the found object, so no matter how deeply nested it'll find it quickly:

function findObjectByKeyValue($array, $key, $value){
    $object = [];
    $string = json_encode($array);
    $foundPosition = strpos($string, '"' . $key . '":"' . $value . '"');

    if( $foundPosition ){
        $prevBracketPos = strrpos(substr($string, 0, $foundPosition), '{');

        if( $prevBracketPos ){
            $nextBracketPos = strpos($string, '}', $foundPosition);

            if( $nextBracketPos ){
                $brackets = 0;

                while( strpos(substr($string, $foundPosition, $nextBracketPos - $foundPosition), '{') &&
                        substr_count(substr($string, $foundPosition, $nextBracketPos - $foundPosition), '{') > $brackets
                    ){
                    $lenToAdd = strlen(substr($string, $foundPosition, $nextBracketPos - $foundPosition + 1));
                    $nextBracketPos = strpos($string, '}', $foundPosition + $lenToAdd);
                    $brackets++;
                }

                $substr = substr($string, $prevBracketPos, $nextBracketPos - $prevBracketPos + 1);

                // Confirm it's wrapped with brackets before we decode
                if( substr($substr, 0, 1) === '{' && substr($substr, -1, 1) === '}' ){
                    $object = json_decode($substr, true);
                }
            }
        }
    }

    return $object;
}

Example:

$arr = [{
    "items":
    {
        "1": [
        {
            "id": "621eaf06062cd",
            "nestedItem":
            {
                "id": "123",
                "nestedItem":
                {
                    "id": "456",
                    "nestedItem":
                    {
                        "id": "789"
                    }
                }
            }
        }],
        "2": [
        {
            "id": "621eb58de7364",
        }],
        "3": [
        {
            "id": "62226910716af",
        }]
    }
}];
echo findObjectByKeyValue($arr, 'id', '123');
/* {
    "id": "123",
    "nestedItem":
    {
        "id": "456",
        "nestedItem":
        {
            "id": "789"
        }
    }
} /*
echo findObjectByKeyValue($arr, 'id', '621eaf06062cd');
/* {
    "id": "621eaf06062cd",
    "nestedItem":
    {
        "id": "123",
        "nestedItem":
        {
            "id": "456",
            "nestedItem":
            {
                "id": "789"
            }
        }
    }
} */

The only problem уоu could run into is if there're brackets within as strings in the array.

Max S.
  • 1,383
  • 14
  • 25
0

Works well on associative array :

public function getKeyByParamValue(string $param, mixed $value, array $array): mixed
{
    $keys = $this->getKeysByParamValue($param, $value, $array);

    return empty($keys) ? null : $keys[0];
}

public function getKeysByParamValue(string $param, mixed $value, array $array): ?array
{
    return array_keys(
        array_combine(
            array_keys($array),
            array_column($array, $param)
        ),
        $value
    );
}
Gigoland
  • 1,287
  • 13
  • 10