Barry Ralphs
Barry Ralphs

Reputation: 295

How to merge multiple arrays and interpolate the sums of values if keys don't match?

I'm looking for a function to merge 2 or more arrays into an output array to use on a graph. The X axes (array key) are dates & the Y are Dollars (array value). Sometimes the dates/keys may match up or they may not. Also the lengths or the array may not be the same. I've found examples on how to do this when the keys match: How to sum all column values in multi-dimensional array? But I'd like to interpolate the values when they don't match. I've put this graph together as an example of the input & output I'm try to get.

example graph

The 2 red input arrays would be:

$a1 = array(
     "2019-01-01" => 0
    ,"2019-01-15" => 1000
    ,"2019-02-05" => 2000
    ,"2019-02-19" => 4000
);

$a2 = array(
     "2019-01-22" => 0
    ,"2019-02-05" => 1000
    ,"2019-02-12" => 2000
    ,"2019-02-26" => 3000
);

And the blue output array would be:

Array
(
    [2019-01-01] => 0
    [2019-01-15] => 1000
    [2019-01-22] => 1333
    [2019-02-05] => 3000
    [2019-02-12] => 5000
    [2019-02-19] => 6500
    [2019-02-26] => 7000
)

I've got the interpolate function working:

echo getFeeFromDates('2019-1-15', 1000, '2019-2-5', 2000, '2019-1-22');

function getFeeFromDates ($date1, $fee1, $date2, $fee2, $getDate) {

    $start = strtotime($date1);
    $end = strtotime($date2);
    $find = strtotime($getDate);

    $days_between = ceil(abs($end - $start) / 86400);
    $fee = abs($fee2 - $fee1);
    $feePerDay = $fee / $days_between;

    $findDays = ceil(abs($find - $start) / 86400);
    $myFee = number_format(($feePerDay * $findDays) + $fee1, 2);

    return $myFee;
}

This will return 1333.33 as the interpolated value on the 2019-1-22 date.

I'm just having trouble wrapping my head around looping through the multiple arrays & when to interpolate the value vs. just adding the values. Any help would be appreciated.

Upvotes: 1

Views: 205

Answers (1)

trincot
trincot

Reputation: 350310

You could create a helper function that takes one key/value array and an array of keys, which returns a key/value array that will contain all the keys in the second array, and interpolated values where needed. The array of keys should have all the keys that exist in the first array, but can have more (not less):

function interpolate($a, $keys) {
    foreach($keys as $key) {
        if (key($a) === $key) {
            $prevValue = $result[$key] = current($a);
            $prevKey = $key;
            next($a);
        } else if (empty($prevKey)) {
            $result[$key] = 0;
        } else {
            $result[$key] = current($a) === false ? $prevValue 
                : $prevValue + (current($a) - $prevValue) 
                             * (strtotime($key) - strtotime($prevKey)) 
                             / (strtotime(key($a)) - strtotime($prevKey));
        }
    }
    return $result;
}

Now with this function it is easy to get the desired result. Let's say you have $a1 and $a2 as in your example. Then do:

$keys = array_keys(array_merge($a1, $a2));
sort($keys);

$b1 = interpolate($a1, $keys);
$b2 = interpolate($a2, $keys);
foreach($b1 as $key => $value) {
    $sum[$key] = $value + $b2[$key];
}

$sum will have the desired result.

More than 2 input arrays

When you have an array of such input arrays, let's call it $a, then you can use this code:

$keys = array_keys(array_merge(...$a));
sort($keys);
$b = array_map(function ($a1) use ($keys) {
    return interpolate($a1, $keys);
}, $a);

foreach($keys as $key) {
    $sum[$key] = array_sum(array_column($b, $key));
}

Upvotes: 1

Related Questions