1

How can I loop recursively through the following multi-dimensional array but still calculate the average by Standards? Here is my initial array, then find my code (Looping through each level which is not ideal) and then the Output.

I'm looking for a function that can reduce or improve this code so I can re-utilize to calculate the average for the other levels.

Array
(
    [42715] => Array
        (
            [1886] => Array
                (
                    [1252] => Array
                        (
                            [1016] => Array
                                (
                                    [Standard 1] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 0.5000000
                                            [2] => 1.0000000
                                            [3] => 1.0000000
                                            [4] => 1.0000000
                                            [5] => 1.0000000
                                        )

                                    [1] => 10/24/2017
                                    [Standard 2] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 1.0000000
                                            [2] => 1.0000000
                                        )

                                    [Standard 3] => Array
                                        (
                                            [0] => 0.0000000
                                            [1] => 0.0000000
                                            [2] => 0.0000000
                                        )

                                    [Standard 4] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 1.0000000
                                            [2] => 1.0000000
                                        )

                                    [Standard 5] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 1.0000000
                                            [2] => 0.0000000
                                        )

                                    [Standard 6] => Array
                                        (
                                            [0] => 0.0000000
                                            [1] => 1.0000000
                                            [2] => 1.0000000
                                            [3] => 0.0000000
                                            [4] => 1.0000000
                                            [5] => 1.0000000
                                        )

                                    [Standard 7] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 1.0000000
                                            [2] => 1.0000000
                                        )

                                    [Standard 8] => Array
                                        (
                                            [0] => 0.0000000
                                            [1] => 1.0000000
                                            [2] => 0.0000000
                                        )

                                    [Standard 9] => Array
                                        (
                                            [0] => 0.0000000
                                            [1] => 0.0000000
                                            [2] => 0.0000000
                                        )

                                )

                            [1019] => Array
                                (
                                    [Standard 8] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 0.0000000
                                            [2] => 0.0000000
                                            [3] => 1.0000000
                                            [4] => 0.0000000
                                            [5] => 0.0000000
                                            [6] => 0.0000000
                                        )

                                    [1] => 10/24/2017
                                )

                            [1017] => Array
                                (
                                    [Standard 1] => Array
                                        (
                                            [0] => 0.0000000
                                        )

                                    [1] => 10/24/2017
                                    [Standard 2] => Array
                                        (
                                            [0] => 1.0000000
                                            [1] => 0.0000000
                                        )

                                    [Standard 3] => Array
                                        (
                                            [0] => 0.0000000
                                            [1] => 0.0000000
                                        )

                                    [Standard 4] => Array
                                        (
                                            [0] => 1.0000000
                                        )

                                    [Standard 5] => Array
                                        (
                                            [0] => 0.0000000
                                        )

                                    [Standard 6] => Array
                                        (
                                            [0] => 0.0000000
                                        )

                                    [Standard 7] => Array
                                        (
                                            [0] => 1.0000000
                                        )

                                    [Standard 8] => Array
                                        (
                                            [0] => 0.0000000
                                        )

                                    [Standard 9] => Array
                                        (
                                            [0] => 1.0000000
                                        )

                                )

                        )

                )

I'm calculating the average of 9 Standards with the following code.

$quizrow = array();
foreach ($modulet as $teacherid => $teacherlist) {
    foreach ($teacherlist as $classid => $classlist) {
        foreach ($classlist as $courseid => $quizlist) {
            foreach ($quizlist as $quizid => $stdlist) {

                $count = $total = 0;
                $timemodified   = $stdlist['timemodified'];

                // Remove this element so we only have Standards.
                unset($stdlist['timemodified']);

                // Reorder the array so we get Standard 1, Standard 2, etc...
                ksort($stdlist, 2);
                foreach ($stdlist as $name => $std) {
                    $quizrow[$teacherid][$classid][$courseid][$quizid][0] = btr_get_quiz_name($quizid);
                    $quizrow[$teacherid][$classid][$courseid][$quizid][1] = $timemodified;
                    $quizrow[$teacherid][$classid][$courseid][$quizid][2] = "";

                    // Calculate the average of the Standards.
                    $average = round(array_sum($std) / count($std) * 100, 1) . "%";
                    $quizrow[$teacherid][$classid][$courseid][$quizid][$name] = $average;
                    $total += $average;
                    $count++;
                }

                // The second index will hold the average results for all Standards.
                $quizrow[$teacherid][$classid][$courseid][$quizid][2] = round($total / $count, 1) . "%";
            }
        }
    }
}

OUTPUT:

Array
(
    [42715] => Array
        (
            [1886] => Array
                (
                    [1252] => Array
                    (
                        [1016] => Array
                            (
                                [0] => Name
                                [1] => 10/24/2017
                                [2] => 62%
                                [Standard 1] => 91.7%
                                [Standard 2] => 100%
                                [Standard 3] => 0%
                                [Standard 4] => 100%
                                [Standard 5] => 66.7%
                                [Standard 6] => 66.7%
                                [Standard 7] => 100%
                                [Standard 8] => 33.3%
                                [Standard 9] => 0%
                            )

                        [1019] => Array
                            (
                                [0] => Name
                                [1] => 10/24/2017
                                [2] => 58.7%
                                [Standard 8] => 28.6%
                            )

                        [1017] => Array
                            (
                                [0] => Name
                                [1] => 10/24/2017
                                [2] => 49.3%
                                [Standard 1] => 0%
                                [Standard 2] => 50%
                                [Standard 3] => 0%
                                [Standard 4] => 100%
                                [Standard 5] => 0%
                                [Standard 6] => 0%
                                [Standard 7] => 100%
                                [Standard 8] => 0%
                                [Standard 9] => 100%
                            )

                    )

            )

EDIT UPDATE: The Output array is being organized again, to calculate its average for the next level.

Array
(
    [42715] => Array
        (
            [1886] => Array
                (
                    [1252] => Array
                        (
                            [0] => Array
                                (
                                    [0] => Name
                                    [1] => Name
                                    [2] => Name
                                )

                            [1] => Array
                                (
                                    [0] => 10/24/2017
                                    [1] => 10/24/2017
                                    [2] => 10/24/2017
                                )

                            [2] => Array
                                (
                                    [0] => 62%
                                    [1] => 58.7%
                                    [2] => 49.3%
                                )

                            [Standard 1] => Array
                                (
                                    [0] => 91.7%
                                    [1] => 0%
                                )

                            [Standard 2] => Array
                                (
                                    [0] => 100%
                                    [1] => 50%
                                )

                            [Standard 3] => Array
                                (
                                    [0] => 0%
                                    [1] => 0%
                                )

                            [Standard 4] => Array
                                (
                                    [0] => 100%
                                    [1] => 100%
                                )

                            [Standard 5] => Array
                                (
                                    [0] => 66.7%
                                    [1] => 0%
                                )

                            [Standard 6] => Array
                                (
                                    [0] => 66.7%
                                    [1] => 0%
                                )

                            [Standard 7] => Array
                                (
                                    [0] => 100%
                                    [1] => 100%
                                )

                            [Standard 8] => Array
                                (
                                    [0] => 33.3%
                                    [1] => 28.6%
                                    [2] => 0%
                                )

                            [Standard 9] => Array
                                (
                                    [0] => 0%
                                    [1] => 100%
                                )

                        )

                )

I'm looping again with foreach to the deepest level with a pretty much similar code get the average output:

Array
(
    [42715] => Array
        (
            [1886] => Array
                (
                    [1252] => Array
                        (
                            [0] => Unit of Study: The Price of Fashion – 830L
                            [1] => 10/24/2017
                            [2] => 50.9%
                            [Standard 1] => 45.9%
                            [Standard 2] => 75%
                            [Standard 3] => 0%
                            [Standard 4] => 100%
                            [Standard 5] => 33.4%
                            [Standard 6] => 33.4%
                            [Standard 7] => 100%
                            [Standard 8] => 20.6%
                            [Standard 9] => 50%
                        )

                )

I have to do this until the latest level.

Andres Ramos
  • 330
  • 4
  • 13

2 Answers2

1

[see inline comments for explanation of processes]

Code: (Demo)

function recursive_calculator($array){
    foreach($array as $id=>&$elem){  // make every level modifiable
        if(!isset($elem['timemodified'])){
            $elem=recursive_calculator($elem);  // I am using timemodified to indicate whether there are scores to process
        }else{
            $elem[0]='Quiz Name'; // btr_get_quiz_name($id);
            $elem[1]=$elem['timemodified'];  // store date with a new key
            unset($elem['timemodified']);    // remove old element
            $score_sets=array_filter($elem,function($k){return strpos($k,'Standard ')===0;},ARRAY_FILTER_USE_KEY);  // isolate quiz score subarrays only
            foreach($score_sets as $name=>$scores){
                $elem[$name]=($averages[]=round(array_sum($scores)/count($scores)*100,1))."%"; // double declaration, $averages will be used outside of the loop
            }
            $elem[2]=round(array_sum($averages)/count($averages),1)."%";  // calculate the cumulative average for all sets/quizzes
            ksort($elem,SORT_NATURAL);  // sort the data as desired
        }
    }
    return $array;  // return the full modified data structure
}

$modulet=[
    42715=>[
        1886=>[
            1295=>[
                1166=>[
                    'Standard 1'=>[0.5000000,0.5000000,1.0000000,0.5000000,0.5000000,0.5000000,0.5000000,0.5000000,0.5000000,0.5000000,0.5000000],
                    'timemodified'=>'10/23/2017',
                    'Standard 2'=>[0.0000000,1.0000000,1.0000000,1.0000000,0.0000000,0.0000000,0.0000000,0.0000000,1.0000000,0.0000000,0.0000000]
                ]
            ]
        ]
    ]
];

var_export(recursive_calculator($modulet));

Output:

array (
  42715 => 
  array (
    1886 => 
    array (
      1295 => 
      array (
        1166 => 
        array (
          0 => 'Quiz Name',
          1 => '10/23/2017',
          2 => '45.5%',
          'Standard 1' => '54.5%',
          'Standard 2' => '36.4%',
        ),
      ),
    ),
  ),
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • @AndresRamos I think I have this function working as desired. Please run this on some of your real arrays and let me know if anything isn't quite right. If there is something to fix, please supply me with a new array that causes the failure. If anything needs to be explained further, leave me a comment and I'll try to expand the explanation. (good question, by the way, very clear -- that's rare on SO) – mickmackusa Nov 24 '17 at 06:49
  • It works well for the initial array - which is great! - because of the key `['timemodified']`. But then I re-organize that output to calculate averages in other levels and the new arrays are like [screenshot](http://prntscr.com/heu4c1) at this point the recursive function won't work as expected. I can changed the initial array to have `[1]` instead of `['timemodified']` as index but what change would I need to make in the function? You function rocks! – Andres Ramos Nov 24 '17 at 20:07
  • Please supply me with a new input array that better represents your real data structure. I'll fix up my code for you. ...best for me would be if you loaded the new array directly into my demo link script (replacing the original array declaration) then saved it and sent me the new link as a comment. – mickmackusa Nov 24 '17 at 20:21
  • Thank you @mick, I've edited my original post and added more real data. Also, you can find the new array input here [New Sandbox Demo](http://sandbox.onlinephpfunctions.com/code/2e8f6ad2b2410d5ca9f8ced1bb8fe8922e2f5b25) , I added it below your previous array . I can either modify the index of the initial array for non-Standard indexes or edit the others to have string indexes so they have the same structure. The problem is the first one has a string index `['timemodified']` and the others have `[0],[1][2]` as non-standard indexes. – Andres Ramos Nov 24 '17 at 21:19
  • I only have my phone at the moment and might not get much computer time today, but I will update my answer and notify you asap. – mickmackusa Nov 24 '17 at 21:24
  • Actually I don't think you need recursion for this task, because the levels with Standard data are predictably on the same level every time. Is this not true in all cases? There is nothing wrong with your series of loops. I'll update my answer as promised anyhow. – mickmackusa Nov 24 '17 at 21:29
  • I added more to my original post so you get a deeper understanding. I'm going through levels calculating their averages, so I calculate the deepest average, I reorganize the array, then I walk backwards to the next level to calculate the averages of the previous averages and so on until the first level. No rush from your side, so far I have it working without the recursion but would love to reduce the hundreds of lines it takes. – Andres Ramos Nov 24 '17 at 21:42
  • Why does the input for 1252 have 3 quiz names for the 9 Standards, but 1295 only has 1 for the 9 Standards? Is this some sort of avoidable redundancy? Or will the 3 quiz names be different and then what is their relationship to the 9 Standards. I ask, because I care about the little details. – mickmackusa Nov 24 '17 at 21:50
  • 1
    When I reorganize things in a new array, I do it like `foreach ($values as $n => $v) { $coursearr[$teacherid][$classid][$courseid][$index][] = $value; }` , the `[0]` element has the quiz name so that's why for this case all 3 falls in that index. It is just my fault due I'm not so good with arrays. The only index that is needed besides the Standards is the timemodified usually stored in index [1], what would you suggest? – Andres Ramos Nov 24 '17 at 21:57
  • ...wait a sec... when you say: _I have to do this until the latest level._ Do you mean you are calculating these averages for EVERY level -- like all the way up the the first level (teacher_id)? Do you want to show the averages AT every level along the way? Is this why you are asking for recursion? Please do me a favor and write out EXACTLY the output that you desire. – mickmackusa Nov 25 '17 at 05:54
  • Are we doing this? http://sandbox.onlinephpfunctions.com/code/799beae57d3d684410a6d36e763e9eca766b57ae – mickmackusa Nov 25 '17 at 06:45
  • Here is how the real thing looks like [Screenshot](http://prntscr.com/hf6ch0). Yes, I calculate the Standards average at each level using the data from the deepest level. So once I calculate the initial array, I reorganize the arrays to be able to calculate the next level, does that make sense? Your first solution is accurate. I just need to make the other arrays - once they are re-ordered - consistent with the initial one to use the same function. I hope it makes sense. Thank you! – Andres Ramos Nov 25 '17 at 19:04
  • Please provide me with the exact input array and the exact expected output array that will generate your most recent screenshot. (We've got to move this question to a resolution before I forget about it) – mickmackusa Dec 01 '17 at 15:37
0

If you only need the value, for the whole array, and don't care about the indexes, you can do this:

 <?php                                                                      
    $a = array(array(array(array(array(1,2,3,4,5,99)))));                      
    $it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));       
    $s = array();                                                              
    foreach($it as $v) {                                                       
      $s[] = $v;                                                               
    }                                                                          
    echo(array_sum($s)/count($s));                                             

See: How to Flatten a Multidimensional Array?

Felipe Valdes
  • 1,998
  • 15
  • 26
  • Indexes are very important, not quite sure why I would need to make an array so deep and don't care about the indexes. – Andres Ramos Nov 24 '17 at 19:03