-1

So I have this (simple) method:

/**
 * @param       $needle
 * @param       $haystack
 *
 * @return array
 */
public function recursiveArraySearch($needle, $haystack)
{
    $array = false;

    foreach ($haystack as $key => $value) {
        if ($key === $needle) {
            $array = $value;
        } elseif (is_array($value)) {
            $this->recursiveArraySearch($needle, $value);
        }
    }

    return $array;
}

Which is called like so: $result = $this->recursiveArraySearch('some_index', $configArray);

It am having trouble return it once and for all back to $result`.

If the $needle matches the $key then I just want it to return the value but at the moment it's returning to itself.

Something I haven't actually done yet.

Thanks

UPDATE: When I return the method as the answers suggest and it reached the end of an array node (like a binary tree search) it passes a string in as the $haystack and thus return false.

enter image description here

Data Structure: I may want to get the values of key circled red or I may want the values of the key circled orange?

The function needs to return them of false.

enter image description here

Kal
  • 2,239
  • 6
  • 36
  • 74
  • 1
    In the recursive branch, you are not doing anything with the values returned from the recursive call (i.e. adding it to an array that is being built. Can you illustrate what kind of data structure you intend to return to the primary caller who initiates the initial call to this method? – Mike Brant May 17 '16 at 15:37
  • @MikeBrant I updated the question with the data structure. – Kal May 17 '16 at 15:42
  • 1
    @Kai It wasn't clear to me whether you wanted to return all matches or first match. I have added answer that covers both use cases, as it seems accepted answer only covers use case for finding single match. – Mike Brant May 17 '16 at 17:07

4 Answers4

3

you can do this

  public function recursiveArraySearch($needle, $haystack)
{
    foreach ($haystack as $key => $value) {
        if ($key === $needle) {
            return $value;
        } elseif (is_array($value)) {
            $check = $this->recursiveArraySearch($needle, $value);
            if($check)
               return $check;
        }
    }
return false;
}
Garvit Mangal
  • 900
  • 7
  • 13
2
public function recursiveArraySearch($needle, $haystack)
{
    foreach ($haystack as $key => $value) {
        if ($key === $needle) {
            return $value;
        } elseif (is_array($value)) {
            $result = $this->recursiveArraySearch($needle, $value);
            if ($result !== false){
                return $result;
            }
        }
    }

    return false;
}

When you recurse down you need to check the result and return only if an item was found. If nothing was found then you need to let the loop continue.

This assumes that your array does not contain any boolean values. If it does, you'll need to use an alternate method to avoid confusing a false value for not found.

kicken
  • 2,122
  • 14
  • 17
  • Beautiful. That did the trick, knew there was something simple I was missing. – Kal May 17 '16 at 15:46
  • This is not correct. You don't want to simply return the result of the recursive search without appending it to existing match results. That is unless you only want to return first found value that matches the key. I am guessing this is not the cass since OP envisioned returning an array of results. – Mike Brant May 17 '16 at 16:00
1

I edited this answer to fit your needs.

function findKey($array, $keySearch)
{
    foreach ($array as $key => $item) {
        if ($key == $keySearch) {
            return $item;
        }
        else {
            if (is_array($item)) {
                $keyFound = findKey($item, $keySearch);
                if( $keyFound != false ) {
                    return $keyFound;
                }
            }
        }
    }
    return false;
}
Community
  • 1
  • 1
Mark
  • 3,224
  • 2
  • 22
  • 30
  • @Kal I edited my answer. Now the function only returns false when the key is not found. – Mark May 17 '16 at 15:36
0

A number of problems here. First and foremost you are not assigning the data returned from the recursive call to any kind of data structure. Also, you should be doing a better job of checking edge conditions. Finally, if your Doc Block says that array is returned, you need to 100% make sure you are returning an array. That is the contract you are making with the caller when they read the documentation on this method, so you should adhere to that.

The example below assumes you are just going to return a numerically indexed array of values to the initial caller. This example includes a merge of recursive results to active array, better handling around input validation, and the consistent return of a numerically-indexed array (with empty array signifying no results).

/**
 * @param mixed $needle Integer or string key value used for recursive search.
 * @param array $haystack Array to be searched.
 *
 * @throws InvalidArgumentException
 *
 * @return array Return numerically-indexed array with empty array if no match.
 */
public function recursiveArraySearch($needle, array $haystack)
{
    // validate that we have a proper needle passed
    if(!is_int($needle) && !is_string($needle)) {
       throw new InvalidArgumentException(
           'Invalid search needle type passed as argument. ' .
           "Integer or string value expected. Value passed:\n" .
           var_export($needle, true)
       );
    }

    $array = [];
    foreach ($haystack as $key => $value) {
        // recursively search if $value is non-empty array
        if(is_array($value) && !empty($value)) {
            array_merge($array, $this->recursiveArraySearch($needle, $value));
        }
        // otherwise, we can make exact string/integer comparison
        else if ($key === $needle) {
            $array[] = $value;
        }
    }

    return $array;
}

Note here that I am assuming you are looking for all matches in the recursive structure. If you are looking for the first match, you can do something like the following, which is a breadth-first search.

/**
 * @param mixed $needle Integer or string key value used for recursive search.
 * @param array $haystack Array to be searched.
 *
 * @throws InvalidArgumentException
 *
 * @return mixed Return values could be mixed since we have no constraint on 
 *    value types in haystack.  Null will be returned on no match, thus 
 *    this function cannot differentiate explicitly defined null values
 *    from no match.
 */
public function recursiveBreadthFirstSingleMatchArraySearch($needle, array $haystack)
{
    // validate that we have a proper needle passed
    if(!is_int($needle) && !is_string($needle)) {
       throw new InvalidArgumentException(
           'Invalid search needle type passed as argument. ' .
           "Integer or string value expected. Value passed:\n" .
           var_export($needle, true)
       );
    }

    // see if there is key match at first level of array
    if(array_key_exists($needle, $haystack)) {
        return $haystack[$needle];
    }

    // iterate through haystack performing recursive search on array until match
    foreach ($haystack as $key => $value) {        
        // recursively search if $value is non-empty array
        if(is_array($value) && !empty($value)) {
            $result = $this->
                recursiveBreadthFirstSingleMatchArraySearch($needle, $value));
            if (!is_null($result)) {
                return $result;
            }
        }
    }

    return null;
}
Mike Brant
  • 70,514
  • 10
  • 99
  • 103