0

I have been going through online study on a resource I quite liked till this very moment. The problem is they want my perfectly working array_reduce function is written "in immutable style". Which means that nothing should mutate inside, even accumulator. Which seems to me like making no sense. So, here is a piece of code I use in that study project they do not want to accept, function for flattening an array:

function flatten(array $tree)
{
    $result = array_reduce($tree, function ($acc, $item) {
        $newValue = is_array($item) ? flatten($item) : $item;
        if (!is_array($newValue)) {
            $acc[] = $newValue;
        } else {
            $acc = array_merge($acc, $newValue);
        }
        return $acc;
    }, []);
    return $result;
}

It works perfectly but this resourse (not free, BTW) does not accept it with notices "Should not use of mutating operators" to the lines where I change $acc.

So I ask them WTF and they answer me: do not change input argument and make a new acc instead and return this new acc from the callback. So I rewrite like this:

function flatten(array $tree)
{
    $result = array_reduce($tree, function ($acc, $item) {
        $accNew = $acc;
        $newValue = is_array($item) ? flatten($item) : $item;
        if (!is_array($newValue)) {
            $accNew[] = $newValue;
        } else {
            $accNew = array_merge($acc, $newValue);
        }
        return $accNew;
    }, []);
    return $result;
}

But this doesn't help, the same "Should not use of mutating operators" notices for the lines where now $accNew is being changed.

What the hell? I'm completely confused. Does this even make sense? Is this an appropriate demand? 'Cause I googled on it and it seems there are pretty much no people on Internet who were interested in using array_reduce "in immutable style"...

  • In order to not change the input (and the acc?!) you should change your line to `$accNew = array_merge($accNew, $newValue);` – IT goldman Nov 18 '22 at 19:43
  • How am I changing the acc by using it in array_merge operation? It seems to me it's not getting changed... And notice relates for the $accNew[] = $newValue; line as well, where original acc is not used at all... – Artem Pokhiliuk Nov 19 '22 at 05:23

1 Answers1

0

I'm not 100% certain how this code gets used, but I think you might even be able to skip writing to the accumulator or a copy of it completely, and just return instead:

function flatten(array $tree): array
{
    return array_reduce(
        $tree,
        static function ($acc, $item) {
            $newValue = is_array($item) ? flatten($item) : $item;
            return array_merge($acc, is_array($newValue) ? $newValue : [$newValue]);
        },
        []
    );
}

Edit: My previous answer didn’t account for previous items in the accumulator, this does.

Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • It's not working because (as it seems to me) `return [$newValue];` is not a correct replacement of `$accNew[] = $newValue; return $accNew;` – Artem Pokhiliuk Nov 19 '22 at 05:39
  • @ArtemPokhiliuk, yeah, thinking about it more I updated it to bring everything into the merge. Performance-wise, the temporary variable might be more optimal – Chris Haas Nov 19 '22 at 14:22
  • Hey man! Thanks a lot, it works, passes that stupid test! Will never forget you 'cause you answered my very first question here and I'm going to light it up in programming in the years to come)) Though didn't quite get what's difference from the standpoint of changing original accumulator between your version and my second version. They both seem to me not changing it. Would be happy to figure it out 'cause I've got many more similar notices in my project and I do not want to post questions here every single time... – Artem Pokhiliuk Nov 19 '22 at 15:18
  • This is just a guess, but if you ignore `array_reduce` completely and just look at the function you are changing one of the provided parameters. For this specific instance it doesn’t matter, but there might come a day in the future with another function where this bites you. “I passed `CHEESE` to the function but it keeps saying `Cheese`”. Or, you accidentally do `if($var = true)` instead of `===`. – Chris Haas Nov 19 '22 at 15:51