0

I would like to accumulate values in an associative array depending on part of the key.

I have tested this foreach loop which works fine in an indexed array:

$b = array();

foreach ($a as $key => $value) {
  if ($key > 0) {
    $b[$key] = $b[$key - 1] + $value;
  }
}

I can't get it to work in an associative array, though...

$a (excerpt)

Array ( 
    [2014-04-22|Paul] => 0 
    [2014-04-28|Paul] => 2 
    [2014-05-13|Paul] => 0
    [2014-06-03|Paul] => 1 
    [2014-06-12|Paul] => 0 
    [2014-08-11|Paul] => 1 
    [2014-08-28|Paul] => 3 
    [2012-05-09|John] => 1 
    [2012-08-29|John] => 2 
    [2012-09-05|John] => 0 
    [2012-09-13|John] => 1 
)

$b (desired result)

Array ( 
    [2014-04-22|Paul] => 0 
    [2014-04-28|Paul] => 2 
    [2014-05-13|Paul] => 2 
    [2014-06-03|Paul] => 3 
    [2014-06-12|Paul] => 3 
    [2014-08-11|Paul] => 4 
    [2014-08-28|Paul] => 7 
    [2012-05-09|John] => 1 
    [2012-08-29|John] => 3 
    [2012-09-05|John] => 3 
    [2012-09-13|John] => 4 
)

In the desired result each value is of 'Paul' and 'John' (and more) is accumulated to the previous one.

Nick
  • 138,499
  • 22
  • 57
  • 95
SVL
  • 37
  • 4
  • It is because the array consists of many persons and it is sorted firstly of the person then on the date. – SVL May 13 '19 at 12:18
  • Notice if you create array of accumulated value you don't need the names to be sorted – dWinder May 13 '19 at 12:37

2 Answers2

0

By using array_walk, check if current name is same as next name, as per that I am resetting value by next.

$result = [];
$tempKey = '';
$arr = array_walk($arr, function($item, $key) use(&$result, &$tempKey, &$total){
    list($date, $name) = explode("|", $key); // explode key
    if(empty($tempKey) || $tempKey != $name){ // if empty or if conflict
        $tempKey = $name; // tempKey for reference
        $total = $item; // total reset to current value
    }else{
        $total += $item; // adding for same name
    }
    $result[$key] = $total; // putting calculated value
});

print_r($result);

Output

Array
(
    [2014-04-22|Paul] => 0
    [2014-04-28|Paul] => 2
    [2014-05-13|Paul] => 2
    [2014-06-03|Paul] => 3
    [2014-06-12|Paul] => 3
    [2014-08-11|Paul] => 4
    [2014-08-28|Paul] => 7
    [2012-05-09|John] => 1
    [2012-08-29|John] => 3
    [2012-09-05|John] => 3
    [2012-09-13|John] => 4
)

Demo.

Rahul
  • 18,271
  • 7
  • 41
  • 60
  • 1
    This will end up being slower than a simple `foreach` because of the overhead of the function call for every value in the array. – Nick May 13 '19 at 12:41
  • @Nick Thanks for look over. You are right actually :) I will take care of this from next time. It's learning for me. – Rahul May 13 '19 at 12:49
  • 1
    If I'm curious about performance I generally compare different methods using the code I showed in [this answer](https://stackoverflow.com/questions/54361923/split-string-into-array-and-append-prev-value/54499945#54499945) – Nick May 13 '19 at 12:50
0

You can do this by keeping track of the name parts of the keys, and resetting the total count when you get to a new name:

$b = array();
$lastname = '';
foreach ($a as $key => $value) {
    list($d, $n) = explode('|', $key);
    if ($n == $lastname) {
        $total += $value;
    }
    else {
        $total = $value;
    }
    $b[$key] = $total;
    $lastname = $n;
}
print_r($b);

Output:

Array ( 
    [2014-04-22|Paul] => 0 
    [2014-04-28|Paul] => 2 
    [2014-05-13|Paul] => 2 
    [2014-06-03|Paul] => 3 
    [2014-06-12|Paul] => 3 
    [2014-08-11|Paul] => 4 
    [2014-08-28|Paul] => 7 
    [2012-05-09|John] => 1 
    [2012-08-29|John] => 3 
    [2012-09-05|John] => 3 
    [2012-09-13|John] => 4 
)

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95