Neta Meta
Neta Meta

Reputation: 4047

trying to sum up the amount of children a parent has in a 2d array and also adding a field all the cells has

hey there I've been trying to solve this for about 3 days now with no avail.

I have a 2d array that looks like:

$testObject = array(
    array(
        "id"=> 1,
        "parentID"=> 0,
        "insuCount"=> 300,
        "totalInsuCount"=> '',
        "childrenCount" => ''
    ),
    array(
        "id"=> 21,
        "parentID"=> 1,
        "insuCount"=> 136,
        "totalInsuCount"=> '',
        "childrenCount" => ''
    ),
    array(
        "id"=> 52,
        "parentID"=> 21,
        "insuCount"=> 99,
        "totalInsuCount"=> '',
        "childrenCount" => ''
    )
);

The array has a children/parent it also has insuCount, totalInsuCount, childrenCount. i am trying to add up the issuCount from bottom of the hierarchy to the top parent and set the result in totalInsuCount,

Also it will count nested children, like the top parent in my situation has 2 children.

So basically my correct array should look something like:

$testObject = array(
    array(
        "id"=> 1,
        "parentID"=> 0,
        "insuCount"=> 300,
        "totalInsuCount"=> 535,
        "childrenCount" => 2
    ),
    array(
        "id"=> 21,
        "parentID"=> 1,
        "insuCount"=> 136,
        "totalInsuCount"=> 235,
        "childrenCount" => 1
    ),
    array(
        "id"=> 52,
        "parentID"=> 21,
        "insuCount"=> 99,
        "totalInsuCount"=> 300,
        "childrenCount" => 0
    )
);

Anyone has a clue how would i do that, I've been at it for about 3 days now cant figure out how.

Thanks in advanced.

Upvotes: 0

Views: 903

Answers (3)

Joop Eggen
Joop Eggen

Reputation: 109547

Mind I have outgrown PHP. So the following is simplistic code.

  1. Split the task in todos and dones parts.
  2. While todos not empty repeat:
  3. Search a candidate in todos that is noone's parent in the todos.
  4. If found initialize the cummulative fields. Check the dones whether this is someones parent, and accumulate. (Equivalent to checking all, as in todo it is noone's parent.) Remove from todos.
  5. Error in data: If no candidate found in the non-empty todos, someone is its own grand-parent.

So:

$todo = array(); // Array of indices into $testObject.
for ($i = 0; $i < count($testObject); ++$i) {
    $todo[] = $i;
}

while (count($todo) > 0) {
    $didOne = FALSE;
    foreach ($todo as $i) {
        $id = $testObject[$i]["id"];
        $isAtBottom = TRUE;
        foreach ($todo as $j) {
            if ($j != $i && $testObject[$j]["parentID"] == $id) {
                $isAtBottom = FALSE;
                break;
            }
        }
        if ($isAtBottom) {
            $didOne = TRUE;
            $testObject[$i]["totalInsuCount"] = $testObject[$i]["insuCount"];
            $testObject[$i]["childrenCount"] = 0;
            for ($k = 0; $k < count($testObject); ++$k) { // Walk done items
                if ($testObject[$k]["id"] != $id 
                    && $testObject[$k]["parentID"] == $id) {
                    $testObject[$i]["totalInsuCount"] +=
                        $testObject[$k]["totalInsuCount"];
                    ++$testObject[$i]["childrenCount"];
                }
            }
            array_splice($todo, $i, 1);
            //break; // As $todo changed, simply next while
        }
    }
    if (!$didOne) {
        error_log("Cyclic dependency");
    }
}
print("<pre>\n");
print_r($testObject);
print("\n</pre>\n");

Upvotes: 1

claustrofob
claustrofob

Reputation: 4984

First you have to transform your array into array with keys equal to your IDs. It will allow you to access any element directly by ID:

$testObject = array(
     1 => array(
        "id"=> 1,
        "parentID"=> 0,
        "insuCount"=> 300,
        "totalInsuCount"=> '',
        "childrenCount" => ''
     ),
     21 => array(
        "id"=> 21,
        "parentID"=> 1,
        "insuCount"=> 136,
        "totalInsuCount"=> '',
        "childrenCount" => ''
     ),
     52 => array(
        "id"=> 52,
        "parentID"=> 21,
        "insuCount"=> 99,
        "totalInsuCount"=> '',
        "childrenCount" => ''
     )
);

this is pretty obvious to implement. So i will leave the solution to you.

And now we can easily calculate counts for all parents:

foreach ($testObject as $id => &$data){

    $parentId = $data['parentID'];
    while ($parentId && isset($testObject[$parentId])){
        $parentData = &$testObject[$parentId];

        $parentData["childrenCount"]++;
        $parentData["totalInsuCount"] += $data["insuCount"];

        $parentId = $parentData['parentID'];
    }

    $data["totalInsuCount"] += $data["insuCount"];
}

print_r($testObject);

UPDATE

Test that any children was counted and belongs to some root entry:

$totalChildren = 0;
foreach ($testObject as $data){
    //sum all childrens of root elements
    if (!$data['parentID']) $totalChildren += $data["childrenCount"] + 1;
}   

echo 'Children counted: ' .$totalChildren;
echo "<br>";
echo 'Total elements: ' .sizeof($testObject);

Also i had to prepare the initial array as some entries are self referenced:

$newTestObject = array();
foreach ($testObject as $data){
    if ($data['id'] == $data['parentID']) $data['parentID'] = 0;
    $newTestObject[$data['id']] = $data;
}

$testObject = $newTestObject;

Upvotes: 1

JimL
JimL

Reputation: 2541

It kinda bugs me that I can't just write comments... anyway.

Your last array entry doesn't make sense to me. count = 99 + 0 children values = 300? How should the count work?

Upvotes: 0

Related Questions