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.

2
  • It is because the array consists of many persons and it is sorted firstly of the person then on the date. Commented May 13, 2019 at 12:18
  • Notice if you create array of accumulated value you don't need the names to be sorted Commented May 13, 2019 at 12:37

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

Comments

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.

3 Comments

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 Thanks for look over. You are right actually :) I will take care of this from next time. It's learning for me.
If I'm curious about performance I generally compare different methods using the code I showed in this answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.