在递归搜索多维数组时,获取第一个匹配键的值。

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

这个方法在关联数组中搜索特定的键,并返回与之关联的值。递归出现了一些问题。有什么线索吗?

你怎么不知道它在哪个级别? - Ignacio Vazquez-Abrams
一份[mcve]会让这个问题更好/更清晰。 - mickmackusa
8个回答

57

也许在旧版本的PHP中这样做有些过度,但是在>=5.6(特别是7.0)中,我毫不犹豫地会使用它。

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

此外,自 PHP 5.6 开始,使用生成器可以轻松迭代通过过滤器的所有元素,而不仅仅是第一个元素:
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
}

6
谢谢,这对我很有帮助。请记得如果您不需要进行严格比较键,则将“===”更改为“==”。 - Batandwa
注意,如果在递归中键出现多次,则此代码仅获取第一个键的值。要获取所有值,请修改如下: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
在 PHP 5.6 之后,我会使用生成器来实现这个功能,调用 yield 而不是 return - xPheRe
那会怎么样,@xPheRe? - Marco Aurélio Deleu

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

你需要通过返回false来停止递归深度搜索,并在函数中进行检查。
你可以在此链接中找到更多函数的示例(例如使用RecursiveArrayIterator等): http://php.net/manual/zh/function.array-search.php

如果键值为'0'/零,则会失败。在此之下使用fn 'recursiveFind'。 - phpJs

6

xPheRe提供的答案非常有帮助,但在我的实现中并没有完全解决问题。我们的数据结构中有多个嵌套的关联数组,并且可能会出现任何给定键的多个实例。

为了适应我们的目的,我需要实现一个持有者数组,在遍历整个结构时更新,而不是在第一次匹配时返回。真正的工作是由另一个帖子提供的,但我想说声谢谢并分享我必须覆盖的最后一步。

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

0
上面的最佳解决方案忽略了键重复的情况,只返回第一个值,这里我将所有值都存入一个数组中:(演示)。
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;
}

0

试试这个:

array_walk_recursive(
    $arrayToFindKey, 
    function($value, $key, $matchingKey){
        return (strcasecmp($key, $matchingKey) == 0)? true : false;
    }
    , 'matchingKeyValue'
);

2
array_walk_recursive 可能是 PHP 中最无用的函数之一。 它会跳过所有子数组,因此如果您的 $key 是另一个数组的键,则找不到它。 - John
这个未经解释的答案从来没有起作用,并且至少误导/困惑了3个读者。证据:https://3v4l.org/XA9uZ - mickmackusa

0

我之前遇到过类似的问题,以下是解决方法:

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

这将返回一个包含多维数组中所有匹配键值的数组。我使用电子邮件API动态生成的数组进行了测试。在多个匹配项的情况下,您只需要创建一个简单的foreach循环来按照您想要的方式对数组进行排序。

我注意到我犯的主要错误是在应该使用if-if条件时使用if-ifelse条件。欢迎任何问题或批评,谢谢!


0

我最近遇到了同样的问题,当处理Yii2查询对象时。

你的函数没有起作用的原因是这里不适用返回操作。只需传递一个引用参数来存储值,然后在之后进行任何操作即可。

正如你所看到的,这是一个简单的PHP函数,不依赖于任何库。因此,我认为它值得在上面列出的所有答案中提及。

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


0
这是我的解决方案:
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'];
}

如果找到了键的值(值),则返回一个数组;如果未找到键,则返回一个空数组。
如果您只需要返回它找到的第一个值,可以使用:
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;
}

返回找到的第一个值。
如果未找到键,则返回false
以下是一个示例数组:

$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', ), ), );

第一个函数将返回["haha","hoho","hehe","huhu"],第二个函数将返回"haha"

使用函数迭代的不幸之处在于它不能被有条件地中断。当只寻求一个值时,整个数组必须被遍历。 - mickmackusa
这个答案 很可能不可靠,因为如果键的值为 falsey,则它不能正确返回键的值。 它也无法区分已找到键的 false 与未找到键的 false。 此页面上的其他答案没有遇到同样的错误行为。 - mickmackusa
正如@mickmackusa所指出的,如果值为假值,函数将无法正常工作。这是我特定用例中预期的行为,但并不符合OP的要求。我已经编辑了我的函数来修复这个问题,现在它们完美地工作 - rAthus
这个更新答案中的第二个片段:1. 当未找到所需字符串时,会生成一个警告,2. 不遵守 PSR-12 编码准则,3. 在 array_walk_recursive() 中不需要两个单独的条件,4. 当第一个所需键的值为 null 时不可靠。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接