Home > Mobile >  Making changes on a different property of recursive function
Making changes on a different property of recursive function

Time:07-20

I'm currently developing this code that traverse a hierarchical array which should compute the sub-total of a property called cur_compensation. My issue is that the changes I do is not getting save

  private function computeSubTotal($hierarchy){
            foreach($hierarchy["_children"] as $key => $value){
                if(isset($value["_children"]))
                {
                    static::computeSubTotal($value);
                }
                else{
                    foreach($hierarchy["_children"] as $employee){
                         $employee_cur_compensation = $employee["cur_compensation"] ?? 0;
                        if (!isset($hierarchy["cur_compensation"])) {
                            $hierarchy["cur_compensation"] = 0;
                        } 
                        $hierarchy["cur_compensation"]  = $employee_cur_compensation;
                    }
                    return $hierarchy;
                }
            }
            return $hierarchy;
        }

This is the function so what it does it goes to the deepest node, the deepest node is a value that does not have any _children which mean it doesn't have any sub department (the hierarchy is sorted that the sub department are always on top)

The issue I have, once it reaches the bottom it computes the cur_compensation by looping through the employees of that department and adding it on the department "cur_compensation" property.

The issue is that, it doesn't save any of my changes.

So the purpose of the function is to add up the 'cur_compensation' of each employee/sub-department.

For example ->

$rows = array(
array(
    'name' => "Main",
    'id' => 1,
    'parent_id' => 0,
    'cur_compensation' => 0,
    '_children' => array( 
    array(
       'name' => "Dept A",
       'id' => 2,
        'parent_id' => 1),
     ),
    array(
       'name' => "Dept B",
       'id' => 3,
       'parent_id' => 1,
       '_children' => array( 
          array(
         'name' => "Dept C",
         'cur_compensation' => 30000,
         'id' => 4,
         'parent_id' => 3),
          array(
          'name' => "Employee C",
          'cur_compensation' => 30000,
          'id' => 7,
          'parent_id' => 3
          )
     )),
     array(
     'name' => "Employee A",
     'cur_compensation' => 20000,
     'id' => 5,
     'parent_id' => 1
      ),
     array(
     'name' => "Employee B",
     'cur_compensation' => 30000,
     'id' => 6,
     'parent_id' => 1
      )
     )
 )
);

The result I want to get would be:

 $rows = array(
    array(
        'name' => "Main",
        'id' => 1,
        'parent_id' => 0,
        'cur_compensation' => 120000,
        '_children' => array( 
        array(
           'name' => "Dept A",
           'id' => 2,
           'cur_compensation' => 0,   
            'parent_id' => 1),
         ),
        array(
           'name' => "Dept B",
           'id' => 3,
           'parent_id' => 1,
           'cur_compensation' => 60000,   
           '_children' => array( 
              array(
             'name' => "Dept C",
             'cur_compensation' => 30000,
             'id' => 4,
             'parent_id' => 3),
              array(
              'name' => "Employee C",
              'cur_compensation' => 30000,
              'id' => 7,
              'parent_id' => 3
              )
         )),
         array(
         'name' => "Employee A",
         'cur_compensation' => 30000,
         'id' => 5,
         'parent_id' => 1
          ),
         array(
         'name' => "Employee B",
         'cur_compensation' => 30000,
         'id' => 6,
         'parent_id' => 1
          )
         )
     )
    );

So you would notice that Main and Dept B got the cur_compensation based on the _children property

CodePudding user response:

You need to pass the array as a reference. https://www.php.net/manual/en/language.references.pass.php

PHP passes the array to the function as a pointer, but when you try to update the array, PHP first makes a full copy of the array and updates the copy instead of the original.

Change your function signature to the following and it should be good.

private function computeSubTotal(&$hierarchy){

P.S. You are calling computeSubTotal statically, but the function is not static itself.

CodePudding user response:

There's a few things to make note on here - so I'm going to add comments to your existing code, then provide an example of how you could change it.

(I've formatted the code in each case)

class Example {
    // filler code so that we can call
    public function process($array){
        return $this->computeSubTotal($array);
    }
    
    private function computeSubTotal($hierarchy) {
        // we're not checking whether "_children" property exists before looping on it
        foreach ($hierarchy["_children"] as $key => $value) {
            if (isset($value["_children"])) {
                // we're calling the method, but not doing anything with the return value.
                static::computeSubTotal($value);

                // we can set the original array value instead which will provide a modified copy
                // this can be resolved by uncommenting the line below
                // $hierarchy["_children"][$key] = static::computeSubTotal($value);

                // also note that if this "child" doesn't have any *grand*children
                // then we won't get an updated value due to how this is structured
                // to fix this, you could remove the else wrapping so that the code
                // below runs always
            } else {
                // double looping - we're already looping this array
                // this will cause the end value to increase exponentially
                foreach ($hierarchy["_children"] as $employee) {
                    $employee_cur_compensation = $employee["cur_compensation"] ?? 0;
                    if (!isset($hierarchy["cur_compensation"])) {
                        $hierarchy["cur_compensation"] = 0;
                    }
                    $hierarchy["cur_compensation"]  = $employee_cur_compensation;
                }

                // returning whole array inside the loop is not ideal
                // we have already adjusted the main array
                // comment out this return to prevent that from happening
                return $hierarchy;
            }
        }
        return $hierarchy;
    }
}

$example = new Example;

// calling this on $rows won't give us anything back
// since $rows doesn't contain the property "_children"
$rows = $example->process($rows);

// in this case, you would want to process each array result
// only on this primary array
foreach($rows as $index => $value){
    $rows[$index] = $example->process($value);
}

echo json_encode($rows, JSON_PRETTY_PRINT);

Taking those comments into account, you would end up with something like this:

private function computeSubTotal($hierarchy) {
    // we're not checking whether "_children" property exists before looping on it
    foreach ($hierarchy["_children"] as $key => $value) {
        if (isset($value["_children"])) {
            $hierarchy["_children"][$key] = static::computeSubTotal($value);
        }

        // double looping - we're already looping this array
        // this will cause the end value to increase exponentially
        foreach ($hierarchy["_children"] as $employee) {
            $employee_cur_compensation = $employee["cur_compensation"] ?? 0;
            if (!isset($hierarchy["cur_compensation"])) {
                $hierarchy["cur_compensation"] = 0;
            }
            $hierarchy["cur_compensation"]  = $employee_cur_compensation;
        }
    }
    return $hierarchy;
}

That's closer but still, it's not quite correct due to the double looping.

I've made a simpler version that is hopefully easy to follow:

private function computeSubTotal($hierarchy) {
    if (!isset($hierarchy["_children"])) {
        return $hierarchy;
    }

    // define this outside the loop for clarity
    if (!isset($hierarchy["cur_compensation"])) {
        $hierarchy["cur_compensation"] = 0;
    }

    foreach ($hierarchy["_children"] as $key => $value) {
        // don't need to check for "_children" property
        // as it's now handled in this function
        $updated = static::computeSubTotal($value);

        // reference the $updated array to increment
        // the "cur_compensation" field
        $hierarchy["cur_compensation"]  = $updated["cur_compensation"] ?? 0;

        // update original array
        $hierarchy["_children"][$key] = $updated;
    }

    return $hierarchy;
}

// call like
foreach ($rows as $index => $value) {
    $rows[$index] = static::computeSubTotal($value);
}

You will still need to change how you're passing the $rows variable due to it now containing a "_children" property (as shown in the examples) - either pass each element or add additional logic in that function to handle that.

  •  Tags:  
  • php
  • Related