user7441072
user7441072

Reputation: 279

Calculate totals for each unique 2nd level data set in a 3d array

I am trying to calculate the total of unique DRIVER values in a multidimensional array.

Input:

$distance_covered = [
    '1_JAN_2017' => ['DRIVER_1' => [2, 5, 3],    'DRIVER_2' => [3, 2, 6, 9]],
    '2_JAN_2017' => ['DRIVER_1' => [3, 9],       'DRIVER_3' => [1, 4, 8]],
    '3_JAN_2017' => ['DRIVER_4' => [9],          'DRIVER_1' => [2, 7, 5, 2]], 
    '4_JAN_2017' => ['DRIVER_1' => [5, 3, 3, 2], 'DRIVER_4' => [4, 9, 8, 5]], 
    '5_JAN_2017' => ['DRIVER_2' => [8, 5],       'DRIVER_5' => [3, 9, 7]],
    '6_JAN_2017' => ['DRIVER_5' => [2, 1, 7, 5], 'DRIVER_4' => [1, 9, 6]], 
    '7_JAN_2017' => ['DRIVER_4' => [5, 2, 9],    'DRIVER_3' => [4, 1, 6]],
]; 

Desired result:

[
    'DRIVER_1' => 51,
    'DRIVER_2' => 33,
    'DRIVER_3' => 24,
    'DRIVER_4' => 67,
    'DRIVER_5' => 34
]

This is the sum of distance travelled by each driver in all trips

I tried code like this:

$res = array();
foreach($distance_covered as $value) {
    foreach($value as $key => $number) {
        (!isset($res[$key])) ?
            $res[$key] = $number :
            $res[$key] += $number;
    }
}
print_r($res);

Upvotes: 0

Views: 166

Answers (5)

mickmackusa
mickmackusa

Reputation: 47874

[sarcastic voice] I can't believe everybody overlooked this convoluted function-based one-liner...

Code: (Demo)

var_export(array_map('array_sum', array_merge_recursive(...array_values($distance_covered))));

Output:

array (
  'DRIVER_1' => 51,
  'DRIVER_2' => 33,
  'DRIVER_3' => 24,
  'DRIVER_4' => 67,
  'DRIVER_5' => 34,
)

*this is virtually guaranteed to process slower than any other posted answer.

  1. Remove the first level associative keys (date strings) with array_values()
  2. Unpack the array of arrays with the "splat operator" (...) and feed to array_merge_recursive() to group values
  3. Sum the subarray values by calling array_sum() on each subarray with array_map()

(This is merely an exercise of thinking outside the box.)


Beyond that no one suggested using a null coalescing operator, so I'll post what that can look like:

$driver_totals = [];
foreach ($distance_covered as $daily_log) {
    foreach ($daily_log as $driver => $distances) {
        $driver_totals[$driver] = ($driver_totals[$driver] ?? 0) + array_sum($distances);
    }
}
var_export($driver_totals);

And if you have a special scenario where you only need to know the distance for a single specific driver, you can call upon array_column() like this:

$target_driver = 'DRIVER_4';
$total_distance = 0;
foreach (array_column($distance_covered, $target_driver) as $distances) {
    $total_distance += array_sum($distances);
}
echo "{$target_driver} drove for a distance of {$total_distance}";

*Notice that the order of the drivers within each date array is inconsequential because array_column() is smart enough to find the desired distance subarray.


Finally, if you declare a whitelist array of all possible drivers, you can:

  • control the order of the drivers in the output
  • avoid the iterated isset() conditions
  • ensure that drivers without any distance records are included in the output

Code:

$roster = ['DRIVER_6', 'DRIVER_5', 'DRIVER_4', 'DRIVER_3', 'DRIVER_2', 'DRIVER_1'];
$driver_totals = array_fill_keys($roster, 0);

foreach ($distance_covered as $daily_log) {
    foreach ($daily_log as $driver => $distances) {
        $driver_totals[$driver] += array_sum($distances);
    }
}
var_export($driver_totals);

Upvotes: 0

Nigel Ren
Nigel Ren

Reputation: 57121

You are close, but...

    $res = array ();

    foreach ( $distance_covered as $value ) {
        foreach ( $value as $key=> $driver ) {
            if ( isset($res[$key]) == false ){
                $res[$key]=0;
            }
            $res[$key] += array_sum($driver);
        }
    }

    print_r($res);

The first foreach simply splits the data down to the days. The second one returns elements like $key = 'DRIVER_1' and $driver = array(3, 9). If this is the first time you've encountered this driver, then you need to ensure that the element in $res exists, so set it to 0.

Once you know there is an element there, you can add in the sum of the values ( 3 & 9 in this case ) using the += array_sum($driver) bit. The += is simply adding to rather than having to say a=a+b, you can say a+=b.

Upvotes: 1

VladimirAus
VladimirAus

Reputation: 181

Just traverse through array of arrays.

$distance_covered = array(
  '1_JAN_2017' => array('DRIVER_1' => array(2, 5, 3),'DRIVER_2' => array(3, 2, 6, 9)),
  '2_JAN_2017' => array('DRIVER_1' => array(3, 9), 'DRIVER_3' => array(1, 4, 8)),
  '3_JAN_2017' => array('DRIVER_4' => array(9), 'DRIVER_1' => array(2, 7, 5, 2)),
  '4_JAN_2017' => array('DRIVER_1' => array(5, 3, 3, 2), 'DRIVER_4' => array(4, 9, 8, 5)),
  '5_JAN_2017' => array('DRIVER_2' => array(8, 5), 'DRIVER_5' => array(3, 9, 7)),
  '6_JAN_2017' => array('DRIVER_5' => array(2, 1, 7, 5),  'DRIVER_4' => array(1, 9, 6)),
  '7_JAN_2017' => array('DRIVER_4' => array(5, 2, 9), 'DRIVER_3' => array(4, 1, 6)), );

// Counting.
$merged = [];
foreach ($distance_covered as $day => $stats) {
  foreach ($stats as $driver => $distances) {
    if (!isset($merged[$driver])) {
      $merged[$driver] = 0;
    }
    $merged[$driver] += array_sum($distances);
  }
}

// Display.
echo "<pre>";
print_r($merged);
echo "</pre>";

Upvotes: 1

Praveen Kumar
Praveen Kumar

Reputation: 2408

This one works for me

$res = array();
foreach($distance_covered as $value)//the array which you have given us 
{
    foreach($value as $key => $number) //loop over array of date
    {
        if(!isset($res[$key]))//check if the key exist in over defined array if no then run this
        {
            $res[$key] = array_sum($number);// Sum all distances of that driver
            continue;//set the key and continue the foreach... 
        }
        $res[$key] += array_sum($number);// Sum all distances of that driver    
    }
}

print_r($res);      
die;

And the Output is

Array
(
    [DRIVER_1] => 51
    [DRIVER_2] => 33
    [DRIVER_3] => 24
    [DRIVER_4] => 67
    [DRIVER_5] => 34
)

Upvotes: 1

teo
teo

Reputation: 801

This should work:

$res = array();
foreach($distance_covered as $value) {
    foreach($value as $key => $number) {
        foreach ($number as $n) {
            if (isset($res[$key])) {
                $res[$key] += $n;
            } else {
                $res[$key] = $n;
            }
        }
    }
}

print_r($res);

Upvotes: 1

Related Questions