Reputation: 139
Over the past few days, I've been thinking about how to deal with iterating over keys in a multidimensional array, and I just cannot figure it out. The problem is, I don't know how deep the array might be - I want my code to be able to handle arrays of any depth.
The array itself comes from Advanced Custom Fields, but that's not too important. I need to iterate over the array, run a function on every array key which starts with field_
(to convert it from field_*
to its proper name like post_title
or something), and reconstruct the array with the same structure and values (although the order is not important). The array looks like this:
array (size=12)
'field_5b23d04fef8a6' => string '' (length=0)
'field_5b23d04fefa99' => string '' (length=0)
'field_5b23d04fefe85' => string '' (length=0)
'field_5b23d04ff0077' => string '' (length=0)
'field_5b23d04ff026c' => string '' (length=0)
'field_5b23d0bdb3c1a' => string 'Version 1' (length=9)
'field_5b23d0f48538b' => string '' (length=0)
'field_5b23d0f485772' => string '' (length=0)
'field_5b23d0d52be2d' => string '' (length=0)
'field_5b5ed10a6a7bc' => string '' (length=0)
'field_5b5ed10a6bcf5' =>
array (size=1)
0 =>
array (size=1)
'field_5b5ed10acd264' =>
array (size=1)
0 =>
array (size=6)
'field_5b5ed10b0c9ca' => string '0' (length=1)
'field_5b5ed10b0cce2' => string 'TEST1234' (length=8)
'field_5b5ed10b0d0fd' => string 'Download title' (length=14)
'field_5b5ed10b0d4e2' => string 'EN' (length=2)
'field_5b5ed10b0d72e' => string 'A00' (length=3)
'field_5b5ed10b0df27' => string '887' (length=3)
'field_5b23d088500a4' => string '' (length=0)
What would be the best way to handle this? I've looked at recursive functions and ResursiveArrayIterator already, but none of the examples I found were close enough to let me figure out what I need.
Upvotes: 1
Views: 94
Reputation: 1775
You can recursively call the same function if it finds a nested array like this:
$input = array(
'field_5b23d04fef8a6' => '',
'field_5b23d04fefa99' => '',
'field_5b23d04fefe85' => '',
'field_5b23d04ff0077' => '',
'field_5b23d04ff026c' => '',
'field_5b23d0bdb3c1a' => 'Version 1',
'field_5b23d0f48538b' => '',
'field_5b23d0f485772' => '',
'field_5b23d0d52be2d' => '',
'field_5b5ed10a6a7bc' => '',
'field_5b5ed10a6bcf5' => array(
array(
'field_5b5ed10acd264' => array(
array(
'field_5b5ed10b0c9ca' => '0',
'field_5b5ed10b0cce2' => 'TEST1234',
'field_5b5ed10b0d0fd' => 'Download title',
'field_5b5ed10b0d4e2' => 'EN',
'field_5b5ed10b0d72e' => 'A00',
'field_5b5ed10b0df27' => '887',
),
),
),
),
'field_5b23d088500a4' => '',
);
// recursively re-key array
function dostuff($input){
// always refer to self, even if you rename the function
$thisfunction = __function__;
$output = array();
foreach($input as $key => $value){
// change key
$newkey = (is_string($key) ? preg_replace('/^field_/', 'post_title_', $key) : $key);
// iterate on arrays
if(is_array($value)){
$value = $thisfunction($value);
}
$output[$newkey] = $value;
}
return $output;
}
var_dump(dostuff($input));
So I was looking at this and to my knowledge there is no wrapper function for recursion with callbacks, so here it is:
// general function for recursively doing something
// $input -> array() / the array you wan to process
// $valuefunction -> callable | null / function to run on all values *
// $keyfunction -> callable | null / function to run on all keys *
// * at least one has to defined or there is nothing to do
// callable has two inputs
// $input -> current branch
// $depth -> (int) how deep in the structure are we
// i.e: recursion($some_array, function($branch, $depth){something..}, 'trim');
function recursion($input, $valuefunction = false, $keyfunction = false){
if(!is_array($input)){
trigger_error('Input is '.gettype($input).'. Array expected', E_USER_ERROR);
return null;
}
if(!is_callable($valuefunction)){$valuefunction = false;}
if(!is_callable($keyfunction)){$keyfunction = false;}
if(!$valuefunction && !$keyfunction){
trigger_error('Input is unchanged!', E_USER_WARNING);
return $input;
}
// use recursion internally, so I can pass stuff by reference
// and do the above checks only once.
$recurse = function(&$branch, $depth = 0) use (&$recurse, &$valuefunction, &$keyfunction){
$output = array();
foreach($branch as $key => $value){
$key = $keyfunction ? $keyfunction($key, $depth) : $key;
$output[$key] = (is_array($value) ?
$recurse($value, $depth + 1) :
($valuefunction ?
$valuefunction($value, $depth) :
$value
)
);
}
return $output;
};
return $recurse($input);
}
$valuefunction = function($value, $depth){
return is_string($value) ? $depth.'_'.$value : $value;
};
function keyfunction($key){
return is_string($key) ? preg_replace('/^field_/', 'post_title_', $key) : $key;
}
var_dump(recursion($input, $valuefunction, 'keyfunction'));
Or for your example:
var_dump(recursion($input, 0, function($key){
return is_string($key) ? preg_replace('/^field_/', 'post_title_', $key) : $key;
}));
Upvotes: 1
Reputation:
You can iterate over array recursively like this
function recursiveWalk($array, callable $x)
{
$result = [];
foreach ($array as $key => $value) {
if (is_array($value)) {
$result[$key] = recursiveWalk($value, $x);
} else {
$result[$key] = $x($value);
}
}
return $result;
}
Here example:
$array = [
"aaa" => 1,
"sub1" => [
"xxx" => 2,
"sub2" => [
"yyy" => 3,
"ttt" => 4
]
]
];
print_r(recursiveWalk($array, function ($x) {
return $x + 1;
}));
Array
(
[aaa] => 2
[sub1] => Array
(
[xxx] => 3
[sub2] => Array
(
[yyy] => 4
[ttt] => 5
)
)
)
Upvotes: 0
Reputation: 2379
You could do something like this:
$arr = [
'a',
'b',
'c',
[
'd',
'e',
'f',
[
'g',
'h',
'i',
],
],
];
class MyIterator
{
public function iterate( $array )
{
foreach ( $array as $a ) {
if ( is_array( $a ) ) {
$this->iterate($a);
} else {
echo $a;
}
}
}
}
$iterator = new MyIterator();
$iterator->iterate($arr);
It prints this:
abcdefghi
Upvotes: 0