在多维数组中搜索键并返回它的路径

时间:2014-12-23 19:35:25

标签: php arrays multidimensional-array iterator

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

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

4 个答案:

答案 0 :(得分:4)

仅为完成和未来的访客。结合上面的示例代码和我评论的答案来获取密钥。这是一个工作函数,它将通过一个小的更改返回请求的结果。在我的返回数组中,我返回了密钥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] => 
        )

)

答案 1 :(得分:1)

看起来你假设钥匙永远是唯一的。我不这么认为。因此,您的函数必须返回多个值。我要做的只是写一个递归函数:

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

答案 2 :(得分:1)

已经有RecursiveIteratorIterator使用的答案。我的解决方案可能并不总是最优的,我没有测试估计时间。可以通过重新定义callHasChildren的{​​{1}}方法对其进行优化,以便在找到时不会有子项。但这不属于领域。

以下是您不必使用显式内循环的方法

RecursiveIteratorIterator

注意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 ]; } 。该标志反转迭代的顺序。所以我们可以只使用一个循环来准备路径 - 这实际上是递归迭代器的主要目的。它们隐藏了你的所有内环。

这是working demo

答案 3 :(得分:0)

如果您需要返回所有项与特定键匹配的数组,则可以使用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);
}