Link to home
Start Free TrialLog in
Avatar of Matthew B
Matthew BFlag for Canada

asked on

Sum a column in array based on another column in arrays value

Hi all,

How can i sum a column in an array based on the value of another column in the array?

For example, the following array i need to return Sum of Balance Type Checking and Sum of Balance Type Savings to my view.

my current code in my controller is $checkingSum = array_sum(array_column($arr, 'Balance', 'BalanceType'=>"Checking" ?));

basically im trying to say sum balance where balance type = checking, easy to do in SQL but not sure how with php/arrays

My Array

$arr =
array 176: [

0 => array:3 [
"client" => "Client 123"
"Balance"=>"25"
"BalanceType"="Checking"
]

1 => array:3 [
"Client"=>"Client 123"
"Balance"=>"25"
"BalanceType"=>"Savings"
]

2 => array:3 [
"Client"=>"Client 456"
"Balance"=>"25"
"BalanceType"=>"Checking"
]

3 => array:3 [
"Client"=>"Client 456"
"Balance"=>"25"
"BalanceType"=>"Savings"
]

]
ASKER CERTIFIED SOLUTION
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Matthew B

ASKER

gr8gonzo thank you. That was inredibly helpful!
Worked perfectily.
Your data structure seems to include a level not catered for in the accepted solution - surely you should be iterating over $arr[176].

As an alternative you can use array_reduce() which is specifically for this sort of requirement.
<?php
$arr = [
  176 => [
    [
      "client" => "Client 123",
      "Balance" => "25",
      "BalanceType" => "Checking"
    ],
    [
      "Client"=>"Client 123",
      "Balance"=>"25",
      "BalanceType"=>"Savings"
    ],
    [
      "Client"=>"Client 456",
      "Balance"=>"25",
      "BalanceType"=>"Checking"
    ],
    [
      "Client"=>"Client 456",
      "Balance"=>"25",
      "BalanceType"=>"Savings"
    ]
  ]
];
$sum = array_reduce($arr[176], function($carry, $item) {
  return $item['BalanceType'] == 'Checking' ? $carry + $item['Balance'] : $carry;
}, 0);
echo $sum;

Open in new window

EDIT
Performance comparison - the above takes twice as long to complete as the accepted solution. It took 1000000 iterations to produce a meaningful result so in the scheme of things not too important

array_reduce() : 1.57 seconds
Accepted solution: 0.71 seconds
Julian so youre saying the accepted solution is much faster ?
I find array_reduce() to be more valuable when setting up a more dynamic / user-driven system. For example, I once had a system where the user would select a procedure from a dropdown and hit a button to run that particular procedure on their current data set. However, when you're just setting up a script to do a pre-defined process, my anecdotal experience has been that the raw code approach is faster due to the performance overhead of invoking a function on each element of the array.
Julian so youre saying the accepted solution is much faster ?
Yes by a factor of 2
Gr8gonzo's point is valid but there are times when using script to do something is slower than a PHP function. The latter is implemented in C and runs natively whereas a script solution is interpreted so there are cases where using the PHP function is the better option.

In this case the script was faster.

As a sidebar in the same test process I tested the use of a ternary against an if () statement - the ternary was slightly slower (about 10-15%).
For reference here is the testing script
<?php

$arr = [
  176 => [
    [
      "client" => "Client 123",
      "Balance" => "25",
      "BalanceType" => "Checking"
    ],
    [
      "Client"=>"Client 123",
      "Balance"=>"25",
      "BalanceType"=>"Savings"
    ],
    [
      "Client"=>"Client 456",
      "Balance"=>"25",
      "BalanceType"=>"Checking"
    ],
    [
      "Client"=>"Client 456",
      "Balance"=>"25",
      "BalanceType"=>"Savings"
    ]
  ]
];
function one($data, $iterations)
{
  for($i = 0; $i < $iterations; $i++) {
    $sum = array_reduce($data, function($carry, $item) {
      return $item['BalanceType'] == 'Checking' ? $carry + $item['Balance'] : $carry;
    }, 0);
  }
}

function two($data, $iterations)
{
  for($i = 0; $i < $iterations; $i++) {
    $value = 0;
    foreach($data as $item) {
      $value += $item['BalanceType'] == 'Checking' ? $item['Balance'] : 0;
    }
  }
}

function three($data, $iterations)
{
  for($i = 0; $i < $iterations; $i++) {
    $value = 0;
    foreach($data as $item) {
      if ($item['BalanceType'] == 'Checking') {
        $value += $item['Balance'];
      }
    }
  }
}

function four($data, $iterations)
{
  for($i = 0; $i < $iterations; $i++) {
    $value = 0;
    foreach($data as $item) {
      if ($item['BalanceType'] == 'Checking') {
        $value = $value + $item['Balance'];
      }
    }
  }
}

$watch = array();
$watch[] = microtime(true);
$iterations = 1000000;
one($arr[176], $iterations);
$watch[] = microtime(true);
two($arr[176], $iterations);
$watch[] = microtime(true);
three($arr[176], $iterations);
$watch[] = microtime(true);
four($arr[176], $iterations);
$watch[] = microtime(true);
echo "<pre>";
echo "Array Reduce          : " . ($watch[1] - $watch[0]) . "<br>";
echo "Foreach (ternary)     : " . ($watch[2] - $watch[1]) . "<br>";
echo "Foreach (if / +=)     : " . ($watch[3] - $watch[2]) . "<br>";
echo "Foreach (if / = v + v): " . ($watch[4] - $watch[3]) . "<br>";
echo "</pre>";

Open in new window

Example output
Array Reduce          : 1.5088469982147
Foreach (ternary)     : 0.71356010437012
Foreach (if / +=)     : 0.63449692726135
Foreach (if / = v + v): 0.66030597686768

Open in new window

Working sample here
Matthew, think of a fast-food restaurant that prepares all the meals ahead of time and simply needs to heat one up and hand it to you, and now compare that to cooking a meal at home.

The fast-food option is faster, but a little less flexible (you can't ask the fast-food restaurant to cook your hamburger medium because it's already cooked). This is like a compiled language (such as C++) - the code is prepared in advance, so it runs really fast.

Cooking something at home has all the flexibility that your own kitchen provides, but it will definitely take longer since you're cooking it from scratch. This is like a scripted/interpreted language (such as PHP).

So normally, PHP runs a little slower when it's trying to execute the code that you've written. However, the standard PHP functions are actually little pre-made chunks of compiled code. So when you call a standard PHP function like array_reduce(), you're actually making use of a little bit of really-fast, compiled code among the rest of your scripted code. So often times it is a good idea to see if there's a standard PHP function that will accomplish your task.

For example, if you wanted to find out where the first occurrence of the letter "o" was in "The quick brown fox", you could write some PHP code to do it:
<?php
function findALetterInAString($string, $letter)
{
  $len = strlen($string);
  for($i = 0; $i < $len; $i++)
  {
    if($string[$i] == $letter) { return $i; }
  }
  return null;
}

Open in new window

...or you could use the default PHP function strpos() to do it, which is OODLES faster. Here's a quick test to show how much faster it is by running the two different functions 10 million times each:
<?php
// TEST #1: Using the custom PHP function findALetterInAString()
$tsStart = microtime(true);
for($test = 0; $test < 10000000; $test++)
{
  $result = findALetterInAString("The quick brown fox", "o");
}
$tsEnd = microtime(true);
echo "TEST #1 completed in " . ($tsEnd - $tsStart) . " seconds\n";

// TEST #2: Using the standard PHP function strpos()
$tsStart = microtime(true);
for($test = 0; $test < 10000000; $test++)
{
  $result = strpos("The quick brown fox", "o");
}
$tsEnd = microtime(true);
echo "TEST #2 completed in " . ($tsEnd - $tsStart) . " seconds\n";

Open in new window

The result on my machine:
TEST #1 completed in 5.2903690338135 seconds
TEST #2 completed in 0.38640308380127 seconds

Open in new window


So the standard PHP function was about 14 times faster than the custom function that I wrote.

So there's a definite advantage to using standard PHP functions whenever there is one that fits your needs.

In this case, array_reduce() is a standard PHP function, but its purpose is simply to connect two different pieces of code. So in the cooking world, this is like taking a really fast car to someone else's kitchen to cook a meal from scratch and come back.

Let's say you had to cook 1000 meals from scratch. My solution is more like cooking 1000 meals from scratch in your own kitchen. Using array_reduce() is like taking 1000 really-fast car rides to someone else's kitchen to cook the meal from scratch there. The meals are still being cooked from scratch (custom code), so in this specific situation, array_reduce() is simply adding more overhead to the process.

So you might wonder why you'd ever need to use array_reduce(), but it's great for when that whole cooking process is dynamic. Maybe one time you need to cook in kitchen X and another time you need to cook in kitchen Y. The array_reduce() function is there to give you the fastest ride possible to the desired function/kitchen, no matter where you need to go.
Amazing follow up guys, thanks gr8 and julian, over and above !