Tom Slominski
Tom Slominski

Reputation: 139

Iterating over multidimensional array in PHP

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

Answers (3)

Peter Rakmanyi
Peter Rakmanyi

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

user10156395
user10156395

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

jrswgtr
jrswgtr

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

Related Questions