27
private function find($needle, $haystack) {
    foreach ($haystack as $name => $file) {
        if ($needle == $name) {
            return $file;
        } else if(is_array($file)) { //is folder
            return $this->find($needle, $file); //file is the new haystack
        }               
    }
    
    return "did not find";
}

This method searches for a specific key in an associative array and returns the value associated with it. There's some problem with the recursion. Any clue?

Dharman
  • 30,962
  • 25
  • 85
  • 135

8 Answers8

57

Maybe it was overkill with old versions of PHP, but with >=5.6 (specially with 7.0) I would totally use this without doubt.

function recursiveFind(array $haystack, $needle)
{
    $iterator  = new RecursiveArrayIterator($haystack);
    $recursive = new RecursiveIteratorIterator(
        $iterator,
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($recursive as $key => $value) {
        if ($key === $needle) {
            return $value;
        }
    }
}

Also, as of PHP 5.6, with generators you can easily iterate over all elements which pass the filter, not only the first one:

function recursiveFind(array $haystack, $needle)
{
    $iterator  = new RecursiveArrayIterator($haystack);
    $recursive = new RecursiveIteratorIterator(
        $iterator,
        RecursiveIteratorIterator::SELF_FIRST
    );
    foreach ($recursive as $key => $value) {
        if ($key === $needle) {
            yield $value;
        }
    }
}

// Usage
foreach (recursiveFind($haystack, $needle) as $value) {
    // Use `$value` here
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
xPheRe
  • 2,333
  • 24
  • 33
  • Careful, if the key appears multiple times in the recursion, this only gets the value of the first one. To get them all modify it like this: `function recursiveFind(array $array, $needle) { $iterator = new RecursiveArrayIterator($array); $recursive = new RecursiveIteratorIterator( $iterator, RecursiveIteratorIterator::SELF_FIRST ); $return = []; foreach ($recursive as $key => $value) { if ($key === $needle) { $return[] = $value; } } return $return; } ` – Aditya Mittal May 02 '16 at 20:28
20
function array_search_key($needle_key, $array)
{
    foreach ($array as $key => $value){
        if ($key === $needle_key) {
            return $value;
        }
        if (is_array($value)) {
            if (($result = array_search_key($needle_key,$value)) !== false) {
                return $result;
            }
        }
    }
    return false;
} 

You need to stop the recursive deep search, by return false and then check it in the function.

You can find more examples of functions (like using RecursiveArrayIterator and more) in this link: http://php.net/manual/en/function.array-search.php

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Haim Evgi
  • 123,187
  • 45
  • 217
  • 223
6

The answer provided by xPheRe was extremely helpful, but didn't quite solve the problem in my implementation. There are multiple nested associative arrays in our data structure, and there may be multiple occurrences of any given key.

In order to suit our purposes, I needed to implement a holder array that was updated while traversing the entire structure, instead of returning on the first match. The real work was provided by another poster, but I wanted to say thanks and share the final step that I had to cover.

public function recursiveFind(array $array, $needle)
{
    $iterator  = new RecursiveArrayIterator($array);
    $recursive = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST);
    $aHitList = array();
    foreach ($recursive as $key => $value) {
        if ($key === $needle) {
            array_push($aHitList, $value);
        }
    }
    return $aHitList;
}
Ben Conley
  • 61
  • 1
  • 1
0

try this:

array_walk_recursive(
    $arrayToFindKey, 
    function($value, $key, $matchingKey){
        return (strcasecmp($key, $matchingKey) == 0)? true : false;
    }
    , 'matchingKeyValue'
);
Ashish Awasthi
  • 1,484
  • 1
  • 19
  • 34
  • 2
    array_walk_recursive is possibly one of the mose useless functions of php. It will skip all sub arrays, so if your $key is the key to another array you'll not find it – John Feb 07 '20 at 21:49
  • This unexplained answer has NEVER worked and has fooled/confused at least 3 readers. Proof: https://3v4l.org/XA9uZ – mickmackusa Apr 04 '23 at 20:56
0

The best solution above misses the case if the key is repeated and only returns the first value, here I get all the values in an array instead: (Demo)

function recursiveFind(array $array, $needle) {
  $iterator = new RecursiveArrayIterator($array);
  $recursive = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST);
  $return = [];
  foreach ($recursive as $key => $value) {
    if ($key === $needle) {
      $return[] = $value;
    }
  } 
  return $return;
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Aditya Mittal
  • 1,681
  • 16
  • 12
0

I recently came across the same issue, when dealing with Yii2 query object.

The reason your function didn't work is that the return action doesn't work here. Just pass a reference parameter to store the value, and do whatever you want afterwards.

As you can see, this is a simple PHP function doesn't rely on any library. So I think its worth to mention with all the answer listed above.

function array_search_by_key_recursive($needle, array $haystack, &$return)
{
   foreach ($haystack as $k => $v) {
      if (is_array($v)) {
        array_search_by_key_recursive($needle, $v, $return);
      } else {
        if($k === $needle){
           $return = $v;
        }
      }
   }
}

array_search_by_key_recursive($needle, array $haystack, $return);

print_r($return);

Parry Huang
  • 111
  • 6
0

I just been through a similar issue and here's what worked for me:

    function searchArrayByKey($haystack, $needle, $i = 0) {
     $result = array();
     foreach($haystack as $key => $value) {
       if (is_array($value)) {
         $nextKey = searchArrayByKey($value, $needle);
         if ($nextKey) {
           return $nextKey;
         }
       }
       if (is_array($value) && array_key_exists($needle, $value)) {
         $result[$i++] = $value[$needle];
       }
     }
     if (empty($result)) {
       return false;
     } else {
       return $result;
     }
   }

This is going to return an array containing the value of all the matching keys it found in the multidimensional array. I tested this with arrays dinamically generated by an e-mail API. In the case of multiple matches, you just need to create a simple foreach loop to sort the array however you want.

I noticed the main mistake I was making was using if-ifelse conditions when I should be using if-if conditions. Any questions or criticism are very welcome, cheers!

Dharman
  • 30,962
  • 25
  • 85
  • 135
0

Here's my solution:

function find_value_by_key($key,$array) {
    $data = array('key'=>$key,'ret'=>array());
    array_walk_recursive($array,function($v,$k) use (&$data) {
        if ($k==$data['key'])
            $data['ret'][] = $v;
    },$data);
    return $data['ret'];
}

Returns an array of the value(s) found, if the key isn't found then an empty array is returned.

If you only need to return the first value it finds you can use:

function find_value_by_key($key,$array) {
        $data = array('key'=>$key);
        array_walk_recursive($array,function($v,$k) use (&$data) {
            if (isset($data['ret']))
                return;
            if ($k==$data['key'])
                $data['ret'] = $v;
        },$data);
        return $data['ret']?:false;
}

Returns the first value found.

If the key isn't found then false is returned.

Example with the following array:

$array = array( 0 => 'A', 1 => 'B', 2 => 'C', 'foo' => 'bar', 'mykey' => 'haha', 'test' => array( 'example' => 'lol', 'mykey' => 'hoho', ), 'random' => array( array( 'mykey' => 'hehe', 'notmykey' => 'topkek', ), array( 'mykey' => 'huhu', 'notmykey' => 'topkek', ), ), );

First function would return ["haha","hoho","hehe","huhu"], second one would return "haha"

rAthus
  • 810
  • 9
  • 17
  • This answer is [provably unreliable](https://3v4l.org/ZsZkj) because it does not correctly return a key's value if the value is falsey. It also fails to differentiate between `false` from a found key versus `false` from a not found key. Other answers on this page do not suffer the same buggy behavior. – mickmackusa Apr 04 '23 at 21:11
  • As pointed out by @mickmackusa the functions didn't work if the value was falsey, that was the intented behaviour in a specific use case I had but not what OP asked for, I edited my functions to fix that and they now [work perfectly](https://3v4l.org/bAroR) – rAthus Aug 25 '23 at 13:04
  • The 2nd snippet in this updated answer: 1. [Generates a Warning](https://3v4l.org/8rD5p) when the sought string is not found, 2. Does not obey PSR-12 coding guidelines, 3. Does not need two separate conditions inside of `array_walk_recursive()`, and 4. Is not trustworthy when the first sought key's value is `null`. – mickmackusa Aug 26 '23 at 13:17