PHP Conference Japan 2024

array_walk_recursive

(PHP 5, PHP 7, PHP 8)

array_walk_recursive递归地将用户函数应用于数组的每个成员

描述

array_walk_recursive(数组|对象 &$array, 可调用 $callback, 混合 $arg = null): true

将用户定义的 callback 函数应用于 array 的每个元素。此函数将递归到更深的数组中。

参数

array

输入数组。

callback

通常,callback 接收两个参数。第一个参数是 array 参数的值,第二个参数是键/索引。

注意:

如果 callback 需要使用数组的实际值,请将 callback 的第一个参数指定为 引用。然后,对这些元素进行的任何更改都将在原始数组本身中进行。

arg

如果提供了可选的 arg 参数,它将作为第三个参数传递给 callback

返回值

始终返回 true

变更日志

版本 描述
8.2.0 返回类型现在为 true;以前是 布尔值

示例

示例 #1 array_walk_recursive() 示例

<?php
$sweet
= array('a' => 'apple', 'b' => 'banana');
$fruits = array('sweet' => $sweet, 'sour' => 'lemon');

function
test_print($item, $key)
{
echo
"$key holds $item\n";
}

array_walk_recursive($fruits, 'test_print');
?>

以上示例将输出

a holds apple
b holds banana
sour holds lemon

您可能会注意到键 'sweet' 从未显示过。任何包含 数组 的键都不会传递给函数。

参见

  • array_walk() - 将用户提供的函数应用于数组的每个成员

添加注释

用户贡献的注释 21 条注释

none at of dot your dot biz
11 年前
由于这仅在其中一个示例的输出脚注中提到,我觉得应该详细说明

* 此函数仅访问叶子节点 *

也就是说,如果您有一个包含子数组的数组树,则回调函数只会访问树叶上的纯值。对于树中具有子节点(即子数组)的节点,回调函数永远不会被调用。这使得此函数对于大多数实际情况都不可用。
r
6 年前
如何使用 userdata 参数从递归函数内部修改外部变量。

<?php
$arr
= [
'one' => ['one_one' => 11, 'one_two' => 12],
'two' => 2
];

$counter = 0;

//不会持久化
array_walk_recursive( $arr, function($value, $key, $counter) {
$counter++;
echo
"$value : $counter";
},
$counter);
echo
"counter : $counter";

// 结果
// 11 : 1
// 12 : 1
// 2 : 1
// counter: 0

//仅在相同数组节点中持久化
array_walk_recursive( $arr, function($value, $key, &$counter) {
$counter++;
echo
"$value : $counter";
},
$counter);

// 结果
// 11 : 1
// 12 : 2
// 2 : 1
// counter : 0

//完全持久化。使用 'use' 关键字
array_walk_recursive( $arr, function($value, $key) use (&$counter) {
$counter++;
echo
"$value : $counter";
},
$counter);
echo
"counter : $counter";

// 结果
// 11 : 1
// 12 : 2
// 2 : 3
// counter : 3
ghoffman at salientdigital dot com
13 年前
如果您想要更改现有多维数组的值,如上文注释中所述,您需要将第一个参数指定为引用。这意味着,请确保在 $item 变量前加上一个取地址符 (&),就像下面的 good_example 一样。

不幸的是,给出的 PHP 示例没有这样做。我花了一段时间才弄清楚为什么我的函数没有更改原始数组,即使我传递的是引用。

以下提示:不要从函数中返回任何值!只需更改您通过引用传入的 $item 的值。这有点违反直觉,因为绝大多数函数都会返回值。

<?php
// array_walk_recursive 无法更改您的数组,除非您通过引用传递。
// 不要从您的过滤器函数中返回值,即使乍一看很合乎逻辑!
function bad_example($item,$key){
if(
$key=='test'){
return
'PHP Rocks'; // 不要这样做
}else{
return
$item; // 也别这样做
}
}

// array_walk_recursive 传递引用示例
function good_example(&$item,$key){
if(
$key=='test'){
$item='PHP Rocks'; // 这样做!
}
}

$arr = array('a'=>'1','b'=>'2','test'=>'Replace This');

array_walk_recursive($arr,'bad_example');
var_dump($arr);
/**
* 没有错误,但打印...
* array('a'=>'1','b'=>'2','test'=>'Replace This');
*/

array_walk_recursive($arr,'good_example');
var_dump($arr);
/**
* 打印...
* array('a'=>'1','b'=>'2','test'=>'PHP Rocks');
*/

?>

如果您通过引用传递并在返回之前修改 $item,则从您的函数中返回值确实有效,但是如果您尝试这样做,即使在像这里一样小的示例中,也会非常非常快地耗尽内存。

您可能首先尝试的另一件愚蠢的事情是这样的

<?php
// 抵制这样做的冲动,它不起作用。
$filtered = array_walk_recursive($unfiltered,'filter_function');
?>

当然,$filtered 之后只是 TRUE,而不是您想要的过滤结果。哦,它确实递归地运行了您的函数,但仅更改了局部函数作用域中的所有值,并如文档中所述返回一个布尔值。
php at genjo dot fr
9 年前
我使用 RecursiveIteratorIterator 以及 CATCH_GET_CHILD 参数来迭代叶子节点和父节点,而不是 array_walk_recursive 函数

<?php
// 迭代叶子节点和父节点
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($candidate), RecursiveIteratorIterator::CATCH_GET_CHILD) as $key => $value) {
echo
'My node ' . $key . ' with value ' . $value . PHP_EOL;
}
?>
bradbeattie at gmail dot com
14 年前
描述中写道:“如果 funcname 需要使用数组的实际值,请将 funcname 的第一个参数指定为引用。” 这不一定是很有帮助的,因为您调用的函数可能是内置函数(例如 trim 或 strip_tags)。一种选择是像这样创建这些函数的版本。

<?php
function trim_by_reference(&$string) {
$string = trim($string);
}
?>

这种方法的缺点是,您需要为可能要调用的每个函数创建一个包装函数。相反,我们可以使用 PHP 5.3 的内联函数语法来创建 array_walk_recursive 的新版本。

<?php
/**
* 此函数的行为与 array_walk_recursive 完全相同,除了它假装它调用的函数
* 用其结果替换值。
*
* @param $array 数组的第一个值将作为主要参数传递给 $function
* @param $function 要对数组中的每个元素递归调用的函数
* @param $parameters 可选的数组,其中包含要附加到函数的其他参数
*
* 例如,更改 $array 以获取每个值的第二个、第三个和第四个字符
* array_walk_recursive_referential($array, "substr", array("1","3"));
*/
function array_walk_recursive_referential(&$array, $function, $parameters = array()) {
$reference_function = function(&$value, $key, $userdata) {
$parameters = array_merge(array($value), $userdata[1]);
$value = call_user_func_array($userdata[0], $parameters);
};
array_walk_recursive($array, $reference_function, array($function, $parameters));
}
?>

这里的优点是我们只显式定义一个包装函数,而不是潜在的几十个。
CommentUser
7 年前
以下代码,它将某种扁平化的数组返回到 $results 数组中,在较新版本的 PHP 中会引发错误“PHP Fatal error: Call-time pass-by-reference has been removed”

<?php

$results
= array();

function
example_function ($item, $key, &$arr_values)
{
$arr_values[$key] = $item;
}

array_walk_recursive($data, 'example_function', &$results);

print_r($results);

?>

这可以通过使用匿名函数来修复

<?php

$results
= array();

array_walk_recursive($data, function ($item, $key) use (&$results){$results[$key] = $item;});

print_r($results);

?>
mike at mpsharp dot com
5 年前
这是一个修改叶子节点所属数组的更通用的解决方案。您可以取消设置当前键,或添加同级元素等。

<?php
/**
* array_walk_recursive 的修改版本,它将数组传递给回调函数
* 回调函数可以通过为参数指定引用来修改数组或值。
*
* @param array 输入数组。
* @param callable $callback($value, $key, $array)
*/
function array_walk_recursive_array(array &$array, callable $callback) {
foreach (
$array as $k => &$v) {
if (
is_array($v)) {
array_walk_recursive_array($v, $callback);
} else {
$callback($v, $k, $array);
}
}
}
?>
lincoln dot du dot j at gmail dot com
7 年前
多维数组转换为一维数组

$array=[1=>[2,5=>[4,2],[7,8=>[3,6]],5],4];
$arr=[];
array_walk_recursive($array, function($k){global $arr; $arr[]=$k;});
print_r($arr);

输出

Array ( [0] => 2 [1] => 4 [2] => 2 [3] => 7 [4] => 3 [5] => 6 [6] => 5 [7] => 4 )
gk at anuary dot com
10 年前
array_walk_recursive 本身无法取消设置值。即使您可以通过引用传递数组,在回调函数中取消设置值也只会取消设置该作用域中的变量。

<?php
/**
* http://uk1.php.net/array_walk_recursive 的实现,用于从数组中删除节点。
*
* @param array 输入数组。
* @param callable $callback 函数必须返回一个布尔值,指示是否删除节点。
* @return array
*/
function walk_recursive_remove (array $array, callable $callback) {
foreach (
$array as $k => $v) {
if (
is_array($v)) {
$array[$k] = walk_recursive_remove($v, $callback);
} else {
if (
$callback($v, $k)) {
unset(
$array[$k]);
}
}
}

return
$array;
}
?>

上述函数的最新实现可以在 https://github.com/gajus/marray/blob/master/src/marray.php#L116. 中找到。
Dario
6 年前
我一直在寻找如何更改数组的值,因为在新版本的 PHP 中,你不能再通过引用传递数组了,这里有一个简单的解决方案

<?php
array_walk_recursive
(
$myArray,
function (&
$value) {
if (
/*some condition*/) {
$value = 'New value';
}
}
);
?>

之后,$myArray 将被更改为新值。
cyranix at cyranix dot com
13 年前
我需要在一个结构未知的数组中添加或修改值。我希望使用 array_walk_recursive 来完成这项任务,但因为我还添加了新的节点,所以我提出了一个替代方案。

<?php

/**
* 在数组的任何深度设置键/值对。
* @param $data 要添加/修改的键/值对数组
* @param $array 要操作的数组
*/
function setNodes($data, &$array)
{
$separator = '.'; // 将此设置为任何不会出现在键中的字符串
foreach ($data as $name => $value) {
if (
strpos($name, $separator) === false) {
// 如果数组不包含特殊的分割符字符,则只需设置键/值对。
// 如果 $value 是一个数组,那么你当然可以很好地设置嵌套的键/值对。
$array[$name] = $value;
} else {
// 在这种情况下,我们试图定位一个特定的嵌套节点,而不覆盖任何其他兄弟节点/祖先节点。
// 该节点或其祖先节点可能还不存在。
$keys = explode($separator, $name);
// 设置树的根节点。
$opt_tree =& $array;
// 使用指定的键开始遍历树。
while ($key = array_shift($keys)) {
// 如果当前键之后还有其他键...
if ($keys) {
if (!isset(
$opt_tree[$key]) || !is_array($opt_tree[$key])) {
// 如果此节点不存在,则创建它。
$opt_tree[$key] = array();
}
// 将树的“根”重新定义为此节点(通过引用赋值),然后处理下一个键。
$opt_tree =& $opt_tree[$key];
} else {
// 这是要检查的最后一个键,因此分配值。
$opt_tree[$key] = $value;
}
}
}
}
}

?>

示例用法

<?php

$x
= array();
setNodes(array('foo' => 'bar', 'baz' => array('quux' => 42, 'hup' => 101)), $x);
print_r($x); // $x 的结构与第一个参数相同
setNodes(array('jif.snee' => 'hello world', 'baz.quux.wek' => 5), $x);
print_r($x); // 添加了 $x['jif']['snee'] 并将 $x['baz']['quux'] 修改为 array('wek' => 5)

?>
robin leffmann
9 年前
一个遍历嵌套数组以获取指定键的最后一个设置值的简单解决方案

<?php

$key
= 'blah';
$val = null;
array_walk_recursive( $your_array,
function(
$v, $k, $u) { if($k === $u[0]) $u[1] = $v; },
[
$key ,&$val] );

echo
"$key = $val";

?>
amoffat at amoffat dot com
16 年前
<?
function my_array_map() {
$args = func_get_args();
$arr = array_shift($args);

foreach ($args as $fn) {
$nfn = create_function('&$v, $k, $fn', '$v = $fn($v);');
array_walk_recursive($arr, $nfn, $fn);
}
return $arr;
}
?>

接收一个数组作为第一个参数,以及其他参数作为函数。它递归地将这些函数应用于数组
Rodrigo Guariento
11 年前
简单的 array_walk_recursive

// 变量示例
$myArray = Array(
Array('keyA1' => ' textA1 ', 'keyA2' => ' textA2 '),
Array('keyB1' => ' textB1 ', 'sub' =>
Array('keyB1_sub1' => ' textB1_sub1 '),
Array('keyB1_sub2' => ' textB1_sub2 ')
),
Array('keyC1' => ' textC1 ', 'keyC2' => ' textC2 '),
Array('keyD1' => ' textD1 ', 'keyD2' => ' textD2 '),
Array('keyE1' => ' textE1 ', 'keyE2' => ' textE2 ')
);

// 用于“trim”的函数(或你的函数,使用相同的结构)
function trimming($data) {
if (gettype($data) == 'array')
return array_map("trimming", $data);
else
return trim($data);
}

// 获取数组
$myArray = array_map("trimming", $myArray);

// 显示修剪后的数组
var_dump($myArray);

/*
结果

array (size=5)
0 =>
array (size=2)
'keyA1' => string 'textA1' (length=6)
'keyA2' => string 'textA2' (length=6)
1 =>
array (size=3)
'keyB1' => string 'textB1' (length=6)
'sub' =>
array (size=1)
'keyB1_sub1' => string 'textB1_sub1' (length=11)
0 =>
array (size=1)
'keyB1_sub2' => string 'textB1_sub2' (length=11)
2 =>
array (size=2)
'keyC1' => string 'textC1' (length=6)
'keyC2' => string 'textC2' (length=6)
3 =>
array (size=2)
'keyD1' => string 'textD1' (length=6)
'keyD2' => string 'textD2' (length=6)
4 =>
array (size=2)
'keyE1' => string 'textE1' (length=6)
'keyE2' => string 'textE2' (length=6)

*/
gabrielu at hotmail dot com
18 年前
我决定在之前 PHP 4 兼容版本的 array_walk_recursive() 上进行补充,以便它可以在类中和作为独立函数使用。这两个实例都由以下函数处理,该函数是我从 omega13a at sbcglobal dot net 修改而来。

以下示例用于在类中使用。要将其用作独立函数,请将其从类中取出并重命名。(示例:array_walk_recursive_2)

<?php
class A_Class {

function
array_walk_recursive(&$input, $funcname, $userdata = '') {
if(!
function_exists('array_walk_recursive')) {
if(!
is_callable($funcname))
return
false;

if(!
is_array($input))
return
false;

foreach(
$input as $key=>$value) {
if(
is_array($input[$key])) {
if(isset(
$this)) {
eval(
'$this->' . __FUNCTION__ . '($input[$key], $funcname, $userdata);');
} else {
if(@
get_class($this))
eval(
get_class() . '::' . __FUNCTION__ . '($input[$key], $funcname, $userdata);');
else
eval(
__FUNCTION__ . '($input[$key], $funcname, $userdata);');
}
} else {
$saved_value = $value;

if(
is_array($funcname)) {
$f = '';
for(
$a=0; $a<count($funcname); $a++)
if(
is_object($funcname[$a])) {
$f .= get_class($funcname[$a]);
} else {
if(
$a > 0)
$f .= '::';
$f .= $funcname[$a];
}
$f .= '($value, $key' . (!empty($userdata) ? ', $userdata' : '') . ');';
eval(
$f);
} else {
if(!empty(
$userdata))
$funcname($value, $key, $userdata);
else
$funcname($value, $key);
}

if(
$value != $saved_value)
$input[$key] = $value;
}
}
return
true;
} else {
array_walk_recursive($input, $funcname, $userdata);
}
}

function
kv_addslashes(&$v, $k) {
$v = addslashes($v);
}
}
?>

用法
<?php
$arr
= array(
'a' => '"Hello World"',
'b' => "'Hello World'",
'c' => "Hello 'Worl\"d",
'd' => array(
'A' => 'H"e"l"l"o" "W"o"r"l"d'
)
);

$class = new A_Class();
$class->array_walk_recursive($arr, array(&$class, 'kv_addslashes'));
print_r($arr);
?>
匿名用户
11 年前
从 PHP 5.3.0 开始,当您在 foo(&$a); 中使用 & 时,您将收到一个警告,提示“调用时按引用传递”已弃用。并且从 PHP 5.4.0 开始,调用时按引用传递已被移除,因此使用它将引发致命错误。
lincoln dot du dot j at gmail dot com
4 年前
普通函数解决方案

//1,2,2,3,6,7,3,1,4,2
$arr=[
[1,2],
[2,3],
6,7,[3,1,[4,2]]
];

function a($array){
static $res=[];
foreach($array as $val){
if(is_array($val)){
a($val);
}else{
$res[]=$val;
}
}
return $res;
}

print_r(a($arr));
匿名用户
7 年前
多维数组转换为一维数组

$array=[1=>[2,5=>[4,2],[7,8=>[3,6]],5],4];
$arr=[];
array_walk_recursive($array, function($k){global $arr; $arr[]=$k;});
print_r($arr);

输出

Array ( [0] => 2 [1] => 4 [2] => 2 [3] => 7 [4] => 3 [5] => 6 [6] => 5 [7] => 4 )
chris at willowsconsulting dot ie
12 年前
要将数组的所有值转换为 UTF8,请执行以下操作

<?php

function convert_before_json(&$item, &$key)
{
$item=utf8_encode($item);
}

array_walk_recursive($your_array,"convert_before_json");

?>
JW
16 年前
此函数存在一个严重错误,截至 PHP 5.2.5 版本仍未修复。调用它之后,它可能会意外修改您的原始数组。请继续阅读,避免数小时的沮丧。

错误在这里:http://bugs.php.net/bug.php?id=42850, 并且看起来它将在 5.3 中修复。

如果您遍历的数组包含其他数组元素,则它们将被转换为引用。即使回调函数不按引用获取其第一个参数,并且不对值进行任何操作,也会发生这种情况。

例如,请尝试以下操作
<?php
$data
= array ('key1' => 'val1', 'key2' => array('key3' => 'val3'));
function
foo($item, $key){}
var_dump($data);
?>

原始数组没有引用。现在尝试以下操作
<?php
array_walk_recursive
($data,'foo');
var_dump($data);
?>

现在 key2 是一个引用,而不仅仅是一个数组。因此,如果您执行以下操作
<?php
function test($item){$item['key2'] = array();}
test($data);
var_dump($data);
?>

您将看到 test 修改了 $data,即使它不应该这样做。

一种解决方法是在调用 array_walk_recursive 后立即对数组进行深复制,如下所示
<?php
function array_duplicate($input) {
if (!
is_array($input)) return $input;
$output = array();
foreach (
$input as $key => $value) {
$output[$key] = array_duplicate($value);
}
return
$output;
}
array_walk_recursive($data,'foo');
$data = array_duplicate($data);
var_dump($data);
?>

执行此操作后,引用将消失。
seductiveapps.com
6 年前
用法
$nd = $newsApp2->dataSources();
//walkArray ($nd, 'walkArray_printKey', 'walkArray_printValue');
// 打印整个数组

$x = chaseToPath ($nd, 'RSS_list/English News',false);
walkArray ($x, 'walkArray_printKey', 'walkArray_printValue');
// 打印 $nd['RSS_list']['English News'] 下的所有内容

function &chaseToPath (&$wm, $path, $create=false) {
//var_dump ($create); die();
//echo '$wm=<pre>'; var_dump ($wm);echo '</pre>'; //die();
//$path = str_replace ('/', '/d/', $path);
//$path .= '/d';
$nodes = explode ('/', $path);
$chase = &chase ($wm, $nodes, $create);

//echo '$wm=<pre>'; var_dump ($wm);echo '</pre>'; die();
/*
$dbg = array (
'$path' => $path,
'$nodes' => $nodes,
'$wm' => $wm,
'$chase' => $chase
);
echo '$dbg=<pre style="background:red;color:yellow;">'; var_dump ($dbg); echo '</pre>';
*/
//die();


$false = false;
if (good($chase)) {
$arr = &result($chase);
return $arr;
} else return $false;
}

function &chase (&$arr, $indexes, $create=false) {
if (false) {
echo 'sitewide/functions.php --- $arr=<pre>'; var_dump ($arr); echo '</pre>';
echo 'sitewide/functions.php --- $indexes=<pre>'; var_dump ($indexes); echo '</pre>';
echo 'sitewide/functions.php --- $create=<pre>'; var_dump ($create); echo '</pre>';
}
$r = &$arr;
foreach ($indexes as $idx) {
//echo 'sitewide/functions.php --- $idx=<pre>'; var_dump ($idx); var_dump (array_key_exists($idx,$r)); var_dump ($r); echo '</pre>';
if (
is_array($r)
&& (
$create===true
|| array_key_exists($idx,$r)
)
) {
if ($create===true && !array_key_exists($idx,$r)) $r[$idx]=array();
//echo 'sitewide/functions.php --- $idx=<pre>'; var_dump ($idx); echo '</pre>';
$r = &$r[$idx];
} else {
$err = array(
'msg' => '无法遍历完整树结构',
'vars' => array(
'$idx--error'=>$idx,
'$indexes'=>$indexes,
'$arr'=>$arr
)
);
badResult (E_USER_NOTICE, $err);
$ret = false; // BUG #2 已修复
return $ret;
}
}

//echo 'sitewide/functions.php --- $r=<pre>'; var_dump ($r); echo '</pre>';
return goodResult($r);
}
To Top