1

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.

2

3 Answers 3

1

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;
}));
Sign up to request clarification or add additional context in comments.

1 Comment

Perfect! Altering the key is what I couldn't get my head around. Thanks :)
0

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

Comments

0

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
                )

        )

)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.