首页 > 解决方案 > 随着时间的推移对两个有序的累积对象值数组求和

问题描述

我试图弄清楚如何对两个数组的累积值求和,这看起来很简单,但复杂的是:其中一个数组可能缺少日期。

当一个有日期值而另一个没有时,我们将一个数组中存在的值与另一个数组的日期的最后一次看到(先前)值相加(我给出的示例更好地说明了这一点) .

例如,给定两个对象数组,其中 data2 中有一个 data1 没有的日期值:

var data1 = [
    {date: "30-08-2019", value: 1},
    {date: "03-09-2019", value: 2},
    {date: "04-09-2019", value: 3}
]

var data2 = [
    {date: "30-08-2019", value: 1},
    {date: "02-09-2019", value: 2},
    {date: "03-09-2019", value: 3},
    {date: "04-09-2019", value: 4}
]

我希望将这两者(data1 + data2)相加的结果是:

var result = [
    {date: "30-08-2019", value: 2}, //{date: "30-08-2019", value: 1} + {date: "30-08-2019", value: 1}
    {date: "02-09-2019", value: 3}, //{date: "30-08-2019", value: 1} + {date: "02-09-2019", value: 2}
    {date: "03-09-2019", value: 5}, //{date: "03-09-2019", value: 2} + {date: "03-09-2019", value: 3}
    {date: "04-09-2019", value: 7}  //{date: "04-09-2019", value: 3} + {date: "04-09-2019", value: 4}
]

由于两个数组都是有序的,我认为的方法是用更多数据循环数组,并将其与数据较少的数组的值相加,跟踪较小数据给出的最后日期值是什么,如下所示:

for(let i = 0; i < biggerData.length; i++){

    //both have values for a date that exists the in bigger date array, so we sum them together
    if(smallerData[i][biggerData[i].date]){
       biggerData[i].value+=smallerData[i][biggerData[i].date];
       lastValue = smallerData[i][biggerData[i].date];

    //array with less data has a missing date, then sum the last saved value it gave
    }else{
       biggerData[i].value+=lastValue;
    }
}

这种方法有一个问题,如果较小的数组有一个较大的数组没有的日期怎么办?在这种情况下,该值不会添加到最终结果中。

当比这更进一步时,我开始像之前展示的那样循环一个数组,然后循环另一个数组以获取丢失的日期,但这似乎太复杂且效率低下。我很确定有一个解决方案可以在一个循环中执行此操作(甚至根本不使用循环)。

我在问是否有人可以为此找到更好的解决方案,我正在用 JavaScript 做这个。

标签: javascriptarrays

解决方案


我使用了一堆辅助变量并将日期转换为易于排序的格式。按时间顺序遍历所有现有日期可以很容易地跟踪每个数组的累积值。排序是低效的部分,因为其余部分具有线性复杂性。您可以利用两个数组都已排序的事实来优化排序,但我懒得在这里这样做:)

// Turn '30-08-2019' into '2019-08-30'
const getSortableDate = (dateString) => dateString.split('-').reverse().join('-');

// Enable direct lookup of values
const mapDatesToValues = (data) => {
    const dates = {};
    data.forEach((item) => {
        dates[getSortableDate(item.date)] = item.value;
    });
    return dates;  
};

// Source data
const data1 = [
    {date: "30-08-2019", value: 1},
    {date: "03-09-2019", value: 2},
    {date: "04-09-2019", value: 3}
];

const data2 = [
    {date: "30-08-2019", value: 1},
    {date: "02-09-2019", value: 2},
    {date: "03-09-2019", value: 3},
    {date: "04-09-2019", value: 4}
];

// values for direct lookup
const dates1 = mapDatesToValues(data1);
const dates2 = mapDatesToValues(data2);

// Chronological order for all existing dates
const allDatesOrdered = Object.keys({ ...dates1, ...dates2 }).sort();

// Helper variables:
let acc1 = 0; // Accumulated value while iterating through data1
let acc2 = 0; // Accumulated value while iterating through data2

let existsIn1;
let existsIn2;

let value1; // Current value while iterating through data1
let value2; // Current value while iterating through data2

allDatesOrdered.forEach((date) => {
    existsIn1 = dates1.hasOwnProperty(date);
    existsIn2 = dates2.hasOwnProperty(date);

    value1 = dates1[date];
    value2 = dates2[date];

    // Remember accumulated values
    if (existsIn1) {
        acc1 = value1;
    }
    if (existsIn2) {
        acc2 = value2;
    }

    if (existsIn1 && existsIn2) {
        console.log('sum for', date, 'is', value1 + value2, '(found in both arrays)');
    } else {
        if (existsIn1) {
            console.log('sum for', date, 'is', value1 + acc2, '(only found in data1)');
        } else {
            console.log('sum for', date, 'is', value2 + acc1, '(only found in data2)');
        }
    }
});

推荐阅读