1

Is there a way to mutate an array using array_reduce in PHP?

I'm trying to do something like this:

Given some ordered list of ids:

$array = [["id" => 1], ["id" => 13], ["id" => 4]];

And a tree that has a subtree matching the corresponding ids:

$tree = [
  "id" => 2334,
  "children" => [
    [
      "id" => 111,
      "children" => []
    ],
    [
      "id" => 1, // <- this is a match
      "children" => [
        [
          "id" => 13, // <- this is a match
          "children" => [
            [
              "id" => 4, // <- this is a match
              "children" => []
            ],
            [
              "id" => 225893,
              "children" => []
            ],
            [
              "id" => 225902,
              "children" => []
            ]
          ]
        ]
      ]
    ]
  ]
];

How can I mutate the arrays in that subtree?

I'm currently trying to use array_reduce to walk down the tree and mutate it. However, the mutation isn't being applied to the originally passed in $tree.

array_reduce($array, function (&$acc, $item) {
  $index = array_search($item['id'], array_column($acc['children'], 'id'));
  $acc['children'][$index]['mutated'] = true; // mutation here
  return $acc['children'][$index];
}, $tree);

echo "<pre>";
var_dump($tree); // $tree is unchanged here
echo "</pre>";

Why is $tree not mutated after the running above array_reduce?

Is there a way to use foreach in this case?

4
  • 1
    No, my bad, I misunderstood what you were doing with the return. Commented May 24, 2019 at 0:19
  • @Nick, yeah. My confusion is that I am using &$acc in the callback and then am mutating $acc, but those mutations aren't visible in $tree after array_reduce runs. Commented May 24, 2019 at 0:22
  • The reason $tree isn't being changed is that the $initial value isn't passed by reference (it can't be, since it is allowed to be a literal value e.g. 0 and defaults to NULL) Commented May 24, 2019 at 0:27
  • @Nick Ok that makes sense. Would you recommend another strategy? foreach? Perhaps another array function? Commented May 24, 2019 at 0:30

1 Answer 1

1

I think this function will do what you want. It recurses down $tree, looking for id values that are in $array and setting the mutation flag for those children:

function mutate(&$tree, $array) {
    if (in_array($tree['id'], array_column($array, 'id'))) {
        $tree['mutated'] = true;
    }
    foreach ($tree['children'] as &$child) {
        mutate($child, $array);
    }
}
mutate($tree, $array);
var_export($tree);

Output:

array (
  'id' => 2334,
  'children' => array (
    0 => array (
      'id' => 111,
      'children' => array ( ),
    ),
    1 => array (
      'id' => 1,
      'children' => array (
        0 => array (
          'id' => 13,
          'children' => array (
            0 => array (
              'id' => 4,
              'children' => array ( ),
              'mutated' => true,
            ),
            1 => array (
              'id' => 225893,
              'children' => array ( ),
            ),
            2 => array (
              'id' => 225902,
              'children' => array ( ),
            ),
          ),
          'mutated' => true,
        ),
      ),
      'mutated' => true,
    ), 
  ), 
)

Demo on 3v4l.org

Sign up to request clarification or add additional context in comments.

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.