PHP Conference Japan 2024

array_walk_recursive

(PHP 5, PHP 7, PHP 8)

array_walk_recursive將使用者函式遞迴地應用於陣列的每個成員

描述

array_walk_recursive(array|object &$array, callable $callback, mixed $arg = null): true

將使用者定義的 callback 函式應用於 array 的每個元素。此函式將遞迴到更深的陣列。

參數

array

輸入陣列。

callback

通常,callback 接受兩個參數。第一個參數是 array 參數的值,第二個參數是鍵/索引。

注意:

如果 callback 需要處理陣列的實際值,請將 callback 的第一個參數指定為參考。然後,對這些元素所做的任何變更都將在原始陣列本身中進行。

arg

如果提供了可選的 arg 參數,它將作為第三個參數傳遞給 callback

傳回值

總是傳回 true

變更記錄

版本 描述
8.2.0 傳回型別現在是 true;先前是 bool

範例

範例 #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的鍵都不會傳遞給該函式。

參見

  • array_walk() - 將使用者提供的函式應用於陣列的每個成員

新增筆記

使用者貢獻的筆記 21 筆記

319
none at of dot your dot biz
11 年前
由於這僅在其中一個範例的輸出註腳中提到,我覺得應該明確指出

* 此函式僅拜訪葉節點 *

也就是說,如果您有一個子陣列的子陣列的陣列樹,則只有樹的葉子上的純值會被回調函式拜訪。回調函式永遠不會針對樹中具有子節點(即子陣列)的節點調用。這會導致此函式在大多數實際情況下都無法使用。
26
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
50
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,而不是您想要的篩選結果。喔,它確實以遞迴方式執行了您的函式,但只變更了本機函式範圍內的所有值,並如文件所述傳回布林值。
17
php at genjo dot fr
9 年前
我使用帶有參數 CATCH_GET_CHILD 的 RecursiveIteratorIterator 來疊代葉節點和節點,而不是 array_walk_recursive 函式

<?php
// 疊代葉節點和節點
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($candidate), RecursiveIteratorIterator::CATCH_GET_CHILD) as $key => $value) {
echo
'我的節點 ' . $key . ' 值為 ' . $value . PHP_EOL;
}
?>
8
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));
}
?>

這裡的優點是我們只明確定義了一個包裝函式,而不是可能數十個。
3
CommentUser
7 年前
以下程式碼會將排序後的扁平陣列傳回到 $results 陣列中,在較新版本的 PHP 中會引發錯誤「PHP Fatal error: Call-time pass-by-reference has been removed (PHP 致命錯誤:已移除呼叫時傳參考)」。

<?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);

?>
2
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);
}
}
}
?>
2
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 )
6
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.
1
Dario
6 年前
我一直在尋找如何在新的 PHP 版本中更改陣列的值,因為你不能再通過引用傳遞陣列,這是一個簡單的解決方案

<?php
array_walk_recursive
(
$myArray,
function (&
$value) {
if (
/*某些條件*/) {
$value = '新值';
}
}
);
?>

之後,$myArray 將會被修改成新值。
2
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)

?>
2
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";

?>
1
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;
}
?>

接受一個陣列作為第一個參數,其他參數則為函式。它會將這些函式遞迴地應用於陣列
1
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)

*/
2
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);
?>
2
匿名
11 年前
從 PHP 5.3.0 開始,當您在 foo(&$a) 中使用 & 時,會收到一個警告,指出「呼叫時傳參考」已棄用。 而從 PHP 5.4.0 開始,已移除呼叫時傳參考,因此使用它會引發嚴重錯誤。
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));
0
匿名
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 )
0
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");

?>
-2
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);
?>

執行此操作後,參考就會消失。
-1
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' => 'Could not walk the full tree',
'vars' => array(
'$idx--error'=>$idx,
'$indexes'=>$indexes,
'$arr'=>$arr
)
);
badResult (E_USER_NOTICE, $err);
$ret = false; // BUG #2 squashed
return $ret;
}
}

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