我需要在数组中找到一个特定的键,并返回其值和路径以查找该键。例:
$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;
}
}
只是为了完成和未来的访客。结合上面的示例代码和我评论的答案来获取密钥。这是一个工作函数,它将通过一个小的更改返回请求的结果。在我的返回数组中,我返回键path
和value
,而不是请求的键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] =>
)
)
看起来您假设密钥始终是唯一的。我不这么认为。因此,函数必须返回多个值。我要做的是简单地编写一个递归函数:
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
在使用递
归迭代器迭代器时已经有一个答案。我的解决方案可能并不总是最佳的,我没有测试估计时间。它可以通过重新定义RecursiveIteratorIterator
的callHasChildren
方法进行优化,因此当找到键时将没有子项。但这在域之外。
以下是您不必使用显式内部循环的方法:
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
。此标志颠倒了迭代顺序。因此,我们可以只使用一个循环来准备路径 - 这实际上是递归迭代器的主要目的。它们隐藏了所有的内部循环。
这是工作演示。
如果您需要返回数组,其中包含与某个键匹配的所有项目,您可以使用 php 生成器
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);
}