在PHP的实际开发场景中,经常会遇到需要处理多层嵌套数组的情况,比如从接口返回的多层数据结构中查找某个特定值,同时需要拿到这个值的所在键名,这是非常常见的开发需求。

基础场景:已知多维数组的固定层级
如果提前知道多维数组的嵌套层级是固定的,比如只有两层,那么可以直接使用双层循环遍历查找,这种方式逻辑简单,执行效率也比较高。
<?php
// 定义两层嵌套的多维数组
$data = [
'user1' => ['name' => '张三', 'age' => 20],
'user2' => ['name' => '李四', 'age' => 25],
'user3' => ['name' => '王五', 'age' => 22],
];
$targetValue = '李四';
$resultKey = null;
// 双层循环遍历查找
foreach ($data as $key1 => $subArray) {
foreach ($subArray as $key2 => $value) {
if ($value === $targetValue) {
$resultKey = $key1 . '.' . $key2;
break 2; // 找到后跳出两层循环
}
}
}
if ($resultKey !== null) {
echo '找到目标值,键名是:' . $resultKey;
} else {
echo '未找到目标值';
}通用场景:层级不固定的多维数组
如果多维数组的嵌套层级不固定,或者无法提前预知层级,使用递归遍历是更通用的解决方案,可以遍历任意层级的多维数组。
递归实现方法
递归的核心思路是判断当前元素是否为数组,如果是数组则继续递归遍历,如果不是数组则判断是否与目标值相等,相等则返回当前路径的键名。
<?php
/**
* 递归查找多维数组中的指定值并返回键名路径
* @param array $array 待查找的多维数组
* @param mixed $target 要查找的目标值
* @param array $currentPath 当前遍历的路径键名
* @return string|null 找到则返回键名路径,未找到返回null
*/
function findValueInMultiArray(array $array, $target, array $currentPath = []) {
foreach ($array as $key => $value) {
// 拼接当前路径
$newPath = array_merge($currentPath, [$key]);
if (is_array($value)) {
// 如果是数组,递归查找
$result = findValueInMultiArray($value, $target, $newPath);
if ($result !== null) {
return $result;
}
} else {
// 非数组元素,判断是否与目标值相等
if ($value === $target) {
return implode('.', $newPath);
}
}
}
return null;
}
// 测试使用
$testArray = [
'a' => 1,
'b' => [
'c' => 2,
'd' => [
'e' => 3,
'f' => 'test'
]
],
'g' => 'test'
];
$target = 'test';
$keyPath = findValueInMultiArray($testArray, $target);
if ($keyPath !== null) {
echo '找到目标值,键名路径是:' . $keyPath . PHP_EOL;
} else {
echo '未找到目标值' . PHP_EOL;
}
// 查找第二个test
$target2 = 'test';
// 注意:上述递归只会返回第一个匹配的键名,如果需要返回所有匹配的键名,可以修改返回值逻辑查找所有匹配键名的递归实现
如果需要返回多维数组中所有匹配目标值的键名,而不是只返回第一个,可以调整递归函数的返回逻辑,收集所有匹配的键名路径。
<?php
/**
* 递归查找多维数组中所有匹配的指定值并返回键名路径数组
* @param array $array 待查找的多维数组
* @param mixed $target 要查找的目标值
* @param array $currentPath 当前遍历的路径键名
* @return array 所有匹配的键名路径数组
*/
function findAllValuesInMultiArray(array $array, $target, array $currentPath = []) {
$result = [];
foreach ($array as $key => $value) {
$newPath = array_merge($currentPath, [$key]);
if (is_array($value)) {
// 递归查找子数组
$subResult = findAllValuesInMultiArray($value, $target, $newPath);
$result = array_merge($result, $subResult);
} else {
if ($value === $target) {
$result[] = implode('.', $newPath);
}
}
}
return $result;
}
// 测试
$testArray = [
'a' => 'hello',
'b' => [
'c' => 'hello',
'd' => ['e' => 'world']
]
];
$allKeys = findAllValuesInMultiArray($testArray, 'hello');
echo '所有匹配的键名路径:' . PHP_EOL;
print_r($allKeys);使用迭代器实现查找
除了递归之外,也可以使用PHP的迭代器来实现多维数组的遍历,避免使用递归可能带来的栈溢出问题,适合处理层级非常深的多维数组。
<?php
/**
* 使用迭代器查找多维数组中第一个匹配的指定值并返回键名路径
* @param array $array 待查找的多维数组
* @param mixed $target 要查找的目标值
* @return string|null 找到则返回键名路径,未找到返回null
*/
function findValueWithIterator(array $array, $target) {
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST
);
$path = [];
foreach ($iterator as $key => $value) {
// 记录当前遍历的键路径
$depth = $iterator->getDepth();
$path = array_slice($path, 0, $depth);
$path[] = $key;
if (!is_array($value) && $value === $target) {
return implode('.', $path);
}
}
return null;
}
// 测试
$testArray = [
'x' => 10,
'y' => [
'z' => 20,
'w' => [30, 40, 50]
]
];
$result = findValueWithIterator($testArray, 40);
if ($result !== null) {
echo '找到目标值,键名路径是:' . $result . PHP_EOL;
} else {
echo '未找到目标值' . PHP_EOL;
}不同方法的性能对比和适用场景
不同的实现方式有不同的适用场景,开发者可以根据实际需求选择:
- 固定层级双层循环:适合明确知道数组只有两层嵌套的场景,执行效率最高,代码也最简单。
- 递归实现:适合层级不固定、层级不算特别深的场景,代码逻辑清晰,容易理解,但是层级过深时可能出现栈溢出。
- 迭代器实现:适合层级非常深的多维数组,不会出现栈溢出问题,但是代码相对复杂一些,执行效率略低于固定层级循环。
注意事项
在实际使用时需要注意以下几点:
- 值比较使用
===严格等于,避免类型转换带来的误判,比如字符串"1"和数字1不会被判定为相等。 - 如果数组中存在对象类型的元素,需要先判断元素类型,避免报错。
- 键名路径的分隔符可以根据实际需求调整,比如改成
/或者其他符号。