【问题标题】:Searching for key in multidimensional array and returning path to it在多维数组中搜索键并返回它的路径
【发布时间】:2015-02-21 23:31:36
【问题描述】:

我需要在数组中找到一个特定的键,并返回它的值和找到该键的路径。示例:

$array = array(
  'fs1' => array(
    'id1' => 0,
    'foo' => 1,
    'fs2' => array(
      'id2' => 1,
      'foo2' => 2,
      'fs3' => array(
        'id3' => null,
      ),
      'fs4' => array(
        'id4' => 4,
        'bar' => 1,
      ),
    ),
  ),
);

search($array, 'fs3'); // Returns ('fs1.fs2.fs3', array('id3' => null))
search($array, 'fs2'); // Returns ('fs1.fs2',     array('id2' => 1, ... ))

我已经能够通过数组递归找到正确的键并使用RecursiveArrayIterator(如下所示)返回数据,但我不知道跟踪我当前路径的最佳方法开。

$i = new RecursiveIteratorIterator
    new RecursiveArrayIterator($array),
    RecursiveIteratorIterator::SELF_FIRST);
foreach ($i as $key => value) {
  if ($key === $search) {
    return $value;
  }
}

【问题讨论】:

  • 键总是唯一的吗?当所有键都适合一个维度时,这种方式违背了将数组作为多维的目的。你只想返回第一个实例还是一个数组?
  • @JonathanKuhn 键不一定是唯一的,但是返回第一个结果就可以了。我继承了搜索数组的结构,因此无法更改它。
  • 在我看来,您可以仅使用递归函数和简单的 foreach 循环来跟踪您的进度。
  • 这就是我要建议的——只需使用一个简单的递归函数。
  • 要从recursiveIteratorIterator 获取密钥,请查看:stackoverflow.com/questions/16855211/…

标签: php arrays multidimensional-array iterator


【解决方案1】:

只是为了完成和未来的访客。结合上面的示例代码和我评论的答案以获得密钥。这是一个工作函数,只需稍作改动即可返回请求的结果。在我的返回数组中,我返回键 pathvalue 而不是请求的键 0$search。我觉得这更冗长,更容易处理。

<?php
$array = array(
    'fs1' => array(
        'id1' => 0,
        'foo' => 1,
        'fs2' => array(
            'id2' => 1,
            'foo2' => 2,
            'fs3' => array(
                'id3' => null,
            ),
            'fs4' => array(
                'id4' => 4,
                'bar' => 1,
            ),
        ),
    ),
);

function search($array, $searchKey=''){
    //create a recursive iterator to loop over the array recursively
    $iter = new RecursiveIteratorIterator(
        new RecursiveArrayIterator($array),
        RecursiveIteratorIterator::SELF_FIRST);

    //loop over the iterator
    foreach ($iter as $key => $value) {
        //if the key matches our search
        if ($key === $searchKey) {
            //add the current key
            $keys = array($key);
            //loop up the recursive chain
            for($i=$iter->getDepth()-1;$i>=0;$i--){
                //add each parent key
                array_unshift($keys, $iter->getSubIterator($i)->key());
            }
            //return our output array
            return array('path'=>implode('.', $keys), 'value'=>$value);
        }
    }
    //return false if not found
    return false;
}

$searchResult1 = search($array, 'fs2');
$searchResult2 = search($array, 'fs3');
echo "<pre>";
print_r($searchResult1);
print_r($searchResult2);

输出:

Array
(
    [path] => fs1.fs2
    [value] => Array
        (
            [id2] => 1
            [foo2] => 2
            [fs3] => Array
                (
                    [id3] => 
                )

            [fs4] => Array
                (
                    [id4] => 4
                    [bar] => 1
                )

        )

)
Array
(
    [path] => fs1.fs2.fs3
    [value] => Array
        (
            [id3] => 
        )

)

【讨论】:

  • 你救了我的命。谢谢!我花了很多时间编写/重写函数以及完成这个结果的任何东西,然后经过几个小时的搜索,我找到了这个。
【解决方案2】:

您似乎假设密钥始终是唯一的。我不这么认为。因此,您的函数必须返回多个值。我要做的只是编写一个递归函数:

function search($array, $key, $path='')
{
    foreach($array as $k=>$v)
    {
        if($k == $key) yield array($path==''?$k:$path.'.'.$k, array($k=>$v));
        if(is_array($v))
        { // I don't know a better way to do the following...
            $gen = search($v, $key, $path==''?$k:$path.'.'.$k);
            foreach($gen as $v) yield($v);
        }
    }
}

这是一个递归生成器。它返回一个包含所有命中的生成器。它的使用非常像数组:

$gen = search($array, 'fs3');
foreach($gen as $ret)
    print_r($ret); // Prints out each answer from the generator

【讨论】:

    【解决方案3】:

    RecursiveIteratorIterator 已经使用了答案。我的解决方案可能并不总是最优的,我没有测试估计时间。可以通过重新定义RecursiveIteratorIteratorcallHasChildren方法来优化,这样当key被找到的时候就不会有children了。但这在域之外。

    这是您不必使用显式内部循环的方法:

    function findKeyPathAndValue(array $array, $keyToSearch)
    {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveArrayIterator($array),
            RecursiveIteratorIterator::CHILD_FIRST
        );
    
        $path = [];
        $value = null;
        $depthOfTheFoundKey = null;
        foreach ($iterator as $key => $current) {
            if (
                $key === $keyToSearch
                || $iterator->getDepth() < $depthOfTheFoundKey
            ) {
                if (is_null($depthOfTheFoundKey)) {
                    $value = $current;
                }
    
                array_unshift($path, $key);
                $depthOfTheFoundKey = $iterator->getDepth();
            }
        }
    
        if (is_null($depthOfTheFoundKey)) {
            return false;
        }
    
        return [
            'path' => implode('.', $path),
            'value' => $value
        ];
    }
    

    关注RecursiveIteratorIterator::CHILD_FIRST。该标志颠倒了迭代的顺序。所以我们可以只使用一个循环来准备我们的路径——这实际上是递归迭代器的主要目的。它们对你隐藏了所有的内部循环。

    这里是working demo

    【讨论】:

      【解决方案4】:

      如果您需要返回与某个键匹配的所有项目的数组,您可以使用 php generators

      function recursiveFind(array $haystack, string $needle, $glue = '.'): ?\Generator
      {
          $recursive = new \RecursiveIteratorIterator(
              new \RecursiveArrayIterator($haystack),
              \RecursiveIteratorIterator::SELF_FIRST
          );
      
          foreach ($recursive as $key => $value) {
              //if the key matches our search
              if ($key === $needle) {
                  //add the current key
                  $keys = [$key];
                  //loop up the recursive chain
                  for ($i = $recursive->getDepth() - 1; $i >= 0; $i--) {
                      array_unshift($keys, $recursive->getSubIterator($i)->key());
                  }
      
                  yield [
                      'path' => implode($glue, $keys),
                      'value' => $value
                  ];
              }
          }
      }
      

      用法:

      foreach (recursiveFind($arrayToSearch, 'keyName') as $result) {
          var_dump($result);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-16
        • 2014-11-11
        • 1970-01-01
        • 1970-01-01
        • 2017-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多