(PHP 5, PHP 7, PHP 8)
array_walk_recursive — Apply a user function recursively to every member of an array
Parametersarray
The input array.
callback
Typically, callback
takes on two parameters. The array
parameter's value being the first, and the key/index second.
Note:
If
callback
needs to be working with the actual values of the array, specify the first parameter ofcallback
as a reference. Then, any changes made to those elements will be made in the original array itself.
arg
If the optional arg
parameter is supplied, it will be passed as the third parameter to the callback
.
Always returns true
.
true
now; previously, it was bool. Examples
Example #1 array_walk_recursive() example
<?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');
?>
The above example will output:
a holds apple b holds banana sour holds lemon
You may notice that the key 'sweet
' is never displayed. Any key that holds an array will not be passed to the function.
12 years ago
Since this is only mentioned in the footnote of the output of one of the examples, I feel it should be spelled out:
* THIS FUNCTION ONLY VISITS LEAF NODES *
That is to say that if you have a tree of arrays with subarrays of subarrays, only the plain values at the leaves of the tree will be visited by the callback function. The callback function isn't ever called for a nodes in the tree that subnodes (i.e., a subarray). This has the effect as to make this function unusable for most practical situations.
r ¶
7 years ago
How to modify external variable from inside recursive function using userdata argument.
<?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"; array_walk_recursive( $arr, function($value, $key, &$counter) {
$counter++;
echo "$value : $counter";
}, $counter);array_walk_recursive( $arr, function($value, $key) use (&$counter) {
$counter++;
echo "$value : $counter";
}, $counter);
echo "counter : $counter";
ghoffman at salientdigital dot com ¶
13 years ago
If you are wanting to change the values of an existing multi-dimensional array, as it says above in the note, you need to specify the first argument as a reference. All that means is, be sure to precede the $item variable with an ampersand (&) as in the good_example below.
Unfortunately the PHP example given doesn't do this. It actually took me a while to figure out why my function wasn't changing the original array, even though I was passing by reference.
Here's the tip: Don't return any value from the function! Just change the value of $item that you passed in by reference. This is rather counter-intuitive since the vast majority of functions return a value.
<?php
function bad_example($item,$key){
if($key=='test'){
return 'PHP Rocks'; }else{
return $item; }
}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_walk_recursive($arr,'good_example');
var_dump($arr);
?>
Returning a value from your function does work if you pass by reference and modify $item before you return, but you will eat up memory very, very fast if you try it, even on an example as small as the one here.
One other silly thing you might try first is something like this:
<?php
$filtered = array_walk_recursive($unfiltered,'filter_function');
?>
Of course, $filtered is just TRUE afterwards, not the filtered results you were wanting. Oh, it ran your function recursively alright, but changed all the values in the local function scope only and returns a boolean as the documentation states.
php at genjo dot fr ¶
10 years ago
I use RecursiveIteratorIterator with parameter CATCH_GET_CHILD to iterate on leafs AND nodes instead of array_walk_recursive function :
<?php
foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($candidate), RecursiveIteratorIterator::CATCH_GET_CHILD) as $key => $value) {
echo 'My node ' . $key . ' with value ' . $value . PHP_EOL;
}
?>
mike at mpsharp dot com ¶
5 years ago
Here's a more general solution to modifying the array to which the leaf belongs. You can unset the current key, or add siblings, etc.
<?php
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);
}
}
}
?>
CommentUser ¶
8 years ago
The following code, which returns back a flattened array of sorts into the $results array, in newer versions of PHP raises the error "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);?>
This can be fixed using an anonymous function:
<?php
$results
= array();array_walk_recursive($data, function ($item, $key) use (&$results){$results[$key] = $item;});print_r($results);?>
bradbeattie at gmail dot com ¶
14 years ago
The description says "If funcname needs to be working with the actual values of the array, specify the first parameter of funcname as a reference." This isn't necessarily helpful as the function you're calling might be built in (e.g. trim or strip_tags). One option would be to create a version of these like so.
<?php
function trim_by_reference(&$string) {
$string = trim($string);
}
?>
The downside to this approach is that you need to create a wrapper function for each function you might want to call. Instead, we can use PHP 5.3's inline function syntax to create a new version of array_walk_recursive.
<?php
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));
}
?>
The advantage here is that we only explicitly define one wrapper function instead of potentially dozens.
gk at anuary dot com ¶
11 years ago
array_walk_recursive itself cannot unset values. Even though you can pass array by reference, unsetting the value in the callback will only unset the variable in that scope.
<?php
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;
}
?>
An up to date implementation of the above function can be looked up from https://github.com/gajus/marray/blob/master/src/marray.php#L116.
Dario ¶
6 years ago
I was looking for how to change values of array since you can't pass array by reference anymore in new PHP versions, here is simple solution
<?php
array_walk_recursive(
$myArray,
function (&$value) {
if () {
$value = 'New value';
}
}
);
?>
After that $myArray will be altered with new value.
cyranix at cyranix dot com ¶
13 years ago
I needed to add or modify values in an array with unknown structure. I was hoping to use array_walk_recursive for the task, but because I was also adding new nodes I came up with an alternate solution.
<?phpfunction setNodes($data, &$array)
{
$separator = '.'; foreach ($data as $name => $value) {
if (strpos($name, $separator) === false) {
$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;
}
}
}
}
}?>
Sample usage:
<?php
$x
= array();
setNodes(array('foo' => 'bar', 'baz' => array('quux' => 42, 'hup' => 101)), $x);
print_r($x); setNodes(array('jif.snee' => 'hello world', 'baz.quux.wek' => 5), $x);
print_r($x); ?>
amoffat at amoffat dot com ¶
17 years ago
<?
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;
}
?>
takes an array as the first argument, and functions as the other arguments. it applies those functions recursively to the array
robin leffmann ¶
10 years ago
A simple solution for walking a nested array to obtain the last set value of a specified key:
<?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";?>
gabrielu at hotmail dot com ¶
19 years ago
I decided to add to the previous PHP 4 compatible version of array_walk_recursive() so that it would work within a class and as a standalone function. Both instances are handled by the following function which I modified from omega13a at sbcglobal dot net.
The following example is for usage within a class. To use as a standalone function take it out of the class and rename it. (Example: 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);
}
}
?>
Usage:
<?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);
?>
Anonymous ¶
11 years ago
since PHP 5.3.0, you will get a warning saying that "call-time pass-by-reference" is deprecated when you use & in foo(&$a);. And as of PHP 5.4.0, call-time pass-by-reference was removed, so using it will raise a fatal error.
8 years ago
multidimensional array to single array
$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);
Output
Array ( [0] => 2 [1] => 4 [2] => 2 [3] => 7 [4] => 3 [5] => 6 [6] => 5 [7] => 4 )
hans at loltek dot net ¶
3 months ago
If you want to iterate both arrays and objects interchangeably:
<?php
function array_and_object_walk_recursive(array|object &$array, callable $callback, mixed $arg = null): true
{
$argumentCount = func_num_args();
if ($argumentCount > 3) {
throw new \ArgumentCountError("array_and_object_walk_recursive() expects at most 3 arguments, $argumentCount given");
}
$hasThirdArgument = ($argumentCount === 3);
foreach ($array as $key => &$value) {
if (is_array($value) || is_object($value)) {
if ($hasThirdArgument) {
array_and_object_walk_recursive($value, $callback, $arg);
} else {
array_and_object_walk_recursive($value, $callback);
}
}
if ($hasThirdArgument) {
($callback)($value, $key, $arg);
} else {
($callback)($value, $key);
}
}
return true; }?>
Useful if you have large array-and-object soup in production code.
lincoln dot du dot j at gmail dot com ¶
4 years ago
Normal function solution
//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));
Anonymous ¶
8 years ago
multidimensional array to single array
$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);
Output
Array ( [0] => 2 [1] => 4 [2] => 2 [3] => 7 [4] => 3 [5] => 6 [6] => 5 [7] => 4 )
Rodrigo Guariento ¶
11 years ago
Simple array_walk_recursive:
// var example
$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 ')
);
// function for "trim" (or your function, use same structure)
function trimming($data) {
if (gettype($data) == 'array')
return array_map("trimming", $data);
else
return trim($data);
}
// get array
$myArray = array_map("trimming", $myArray);
// show array trimmed
var_dump($myArray);
/*
RESULT
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)
*/
chris at willowsconsulting dot ie ¶
12 years ago
To convert all values of an array in UTF8, do this:
<?phpfunction convert_before_json(&$item, &$key)
{
$item=utf8_encode($item);
}array_walk_recursive($your_array,"convert_before_json");?>
JW ¶
17 years ago
This function has a serious bug, which is still not fixed as of the PHP 5.2.5 release. After you call it, it can accidentally modify your original array. Save yourself hours of frustration by reading on.
The bug is here: http://bugs.php.net/bug.php?id=42850, and it looks like it will be fixed for 5.3.
If the array that you walk contains other array elements, they will be turned into references. This will happen even if the callback function doesn't take its first argument by reference, and doesn't do anything to the values.
For example, try this:
<?php
$data = array ('key1' => 'val1', 'key2' => array('key3' => 'val3'));
function foo($item, $key){}
var_dump($data);
?>
The original array has no references. Now try this:
<?php
array_walk_recursive($data,'foo');
var_dump($data);
?>
Now key2 is a reference, not just an array. So if you do this:
<?php
function test($item){$item['key2'] = array();}
test($data);
var_dump($data);
?>
you will see that test modifies $data, even though it shouldn't.
One workaround is to immediately make a deep copy of the array after calling array_walk_recursive, like this:
<?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);
?>
After doing that, the references are gone.
seductiveapps.com ¶
7 years ago
usage :
$nd = $newsApp2->dataSources();
//walkArray ($nd, 'walkArray_printKey', 'walkArray_printValue');
// prints the entire array
$x = chaseToPath ($nd, 'RSS_list/English News',false);
walkArray ($x, 'walkArray_printKey', 'walkArray_printValue');
// prints everything under $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);
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4