phparrays

array_map with access to keys


I need to iterate over an array that looks like this:

$myArray = array(
  'key1' => array('subkey' => 'subvalue'),
  'key2' => array('subkey' => 'subvalue'),
)

Into each nested associative array, I want to add a key-value pair based on the outside key, like this:

$myNewArray = array(
  'key1' => array('subkey' => 'subvalue', 'newkey' => 'only for key1'),
  'key2' => array('subkey' => 'subvalue'),
)

Ideally, I'm looking for something like:

$myNewArray = array_map(function($key, $value) {
    if ($key == 'key1') {
        $value['newkey'] = 'only for key1';
    }
    return $value;
}, $myArray);

However, that obviously doesn't work as callback isn't given two parameters but only one. I could do something like this:

$myNewArray = array_map(function($key, $value) {
    if ($key == 'key1') {
        $value['newkey'] = 'only for key1';
    }
    return WHAT??
}, array_keys($myArray), $myArray);

However, what do I return here? It seems to always construct a new array, i.e. discarding my string keys (key1 and key2), while a single-array array_map() keeps them.

I can use array_walk() but it has a rather strange API (flipped parameter order, array passed by reference etc.) so I would generally prefer if this could be achieved using array_map(). Can it?


Solution

  • I'm afraid array_walk() IS the way to do this.

    If you don't like array_walk() and insist on doing it with array_map(), well, it's possible. It involves using also array_keys(), array_values() and array_combine(), it is long and ugly but doable:

    $myNewArray = array_combine(
        array_keys($myArray),
        array_map(
            function($key, $value) {
                if ($key == 'key1') {
                    $value['newkey'] = 'only for key1';
                }
                return $value;
            },
            array_keys($myArray),
            array_values($myArray)     // can omit array_values() and use $myArray
        )
    );
    

    You can also do it using array_reduce() but it's the same mess:

    $myNewArray = array_reduce(
        array_keys($myArray),
        function (array $carry, $key) use ($myArray) {
            $value = $myArray[$key];
            if ($key == 'key1') {
                $value['newkey'] = 'only for key1';
            }
            $carry[$key] = $value;
            return $carry;
        },
        array()
    );
    

    I hope you have a condition more complex than $key == 'key1' because only for this it is not worth it writing complex traversal. Isn't it easier to just access the right element and modify it?

    $myArray['key1']['newkey'] = 'only for key1';