按日期收集JS对象的最快方法

时间:2016-02-09 02:05:57

标签: javascript performance

我的银行交易数据格式如下:

var transactions = {
    food: [
        {
            date: new Date('2016-01-09'),
            amount: 123.45
        },
        {
            date: new Date('2016-01-16'),
            amount: 87.88
        },
        {
            date: new Date('2016-01-23'),
            amount: 99.99
        },
        {
            date: new Date('2016-01-30'),
            amount: 99.99
        }
    ],
    doctor: [
        {
            date: new Date('2016-01-15'),
            amount: 1124.01
        },
        {
            date: new Date('2016-01-16'),
            amount: 656.00
        },
        {
            date: new Date('2016-01-23'),
            amount: 1000.00
        },
    ]
}

即一个看起来像{transaction_type: [array of transactions]}的对象。

我想按日期对这些交易进行分组,所以最后我得到了

var aligned_transactions = [
    {
        date: new Date('2016-01-09'),
        amounts: [123.45]
    },
    {
        date: new Date('2016-01-15'),
        amounts: [1124.01]
    },
    {
        date: new Date('2016-01-16'),
        amounts: [87.88, 656.00]
    },
    {
        date: new Date('2016-01-23'),
        amounts: [99.99, 1000.00]
    },
    {
        date: new Date('2016-01-30'),
        amounts: [99.99]
    }
]

因此,金额现在按日期分组。当然,在实际设置中,有几百种事务类型,每种类型都有数千个事务的数组(大约需要处理100万个事务)。以这种方式转换交易的“最快”方式是什么?这里,最快意味着完成转换所花费的总时间。一个jsperf结果会很棒。

请注意,我已经尝试了几种方法,并且发现嵌套for循环的“明显”方法非常慢:我的机器上有大约12秒,总交易量为100万(Chrome,Ubuntu)。我猜测创建所有这些新对象正在造成损失。

一种有前途的方法是“垂直”切片这些事务列表,这样我就得到了一堆小数组,然后我从中创建对象,并递归地将它们“合并”在一起。这非常快,在我的机器上大约6秒,上面有100万笔交易。不过,我希望有更快的方式。

编辑:

这是嵌套的for循环解决方案:

function align_data(transaction_types) {
    var i, j, transaction, transactions;
    var timestamps = {};
    for (i = 0; i < transaction_types.length; i++) {
        transactions = transaction_types[i];
        for (j = 0; j < transactions.length; j++) {
            transaction = transactions[j];
            if (timestamps[transaction.date]) {
                timestamps[transaction.date].amounts.push(transaction.amount);
            } else {
                timestamps[transaction.date] = {
                    date: transaction.date,
                    amounts: [transaction.amount]
                };
            }
        }
    }

    var aligned = [];
    for (date in timestamps) {
        if (timestamps.hasOwnProperty(date)) {
            aligned.push(timestamps[date]);
        }
    }

    return aligned;
}

3 个答案:

答案 0 :(得分:1)

我刚刚对它进行了测试,在我的计算机上随机设置了100万条记录,运行代码大约需要5-6秒,而以下内容可能只需要半秒:

function align_data(transaction_types) {
    var i, j, transaction, transactions;
    var timestamps = {};
    for (i = 0; i < transaction_types.length; i++) {
        transactions = transaction_types[i];
        for (j = 0; j < transactions.length; j++) {
            transaction = transactions[j];
            if (timestamps[transaction.date.getTime()]) {
                timestamps[transaction.date.getTime()].amounts.push(transaction.amount);
            } else {
                timestamps[transaction.date.getTime()] = {
                    date: transaction.date,
                    amounts: [transaction.amount]
                };
            }
        }
    }

    var aligned = [];
    for (date in timestamps) {
        if (timestamps.hasOwnProperty(date)) {
            aligned.push(timestamps[date]);
        }
    }

    return aligned;
}

我所做的更改是按timestamps而不是transaction.date.getTime()索引transaction.date

答案 1 :(得分:0)

我认为这是一种情况,当你真的应该尝试使用时 WebWorkers.

答案 2 :(得分:0)

如何在所有记录中使用单个累加器对象,然后将其转换为首选数组格式:

function align_data(transactions) {
    var acc = {};
    var d;
    for (var key in transactions) {
        transactions[key].forEach(function (record) { 
            d = record.date.toISOString();
            if (d in acc) {
                acc[d].push(record.amount);
            } else {
                acc[d] = [record.amount];
            }
        });
    }
    var aligned_transactions = [];
    for (var date in acc) {
        aligned_transactions.push({date: new Date(date), amounts: acc[date]});
    }
    return aligned_transactions;
}