3

我有两个清单:

var listA = 
[
    { Id: 2, Date: "2014-11-28", Amount: 30 },
    { Id: 1, Date: "2014-11-27", Amount: 15 },
    { Id: 1, Date: "2014-11-28", Amount: 20 },
];

var listB = 
[
    { Id: 1, Date: "2014-11-27", Amount: 15 },
    { Id: 2, Date: "2014-11-26", Amount: 25 },
];

我想合并两个列表中的数据,按 Id 对它们进行分组并使用结果中每个 Id 的最高日期,并将唯一对象的总数(即具有相同 Id 和日期的对象 - 只能有一个每个日期和 ID 的金额)。

换句话说,我想要这个结果:

// "For ID X, the Amounts up to Date Y = total Z"
[
    {"Id":1,"Date":"2014-11-28","Amount":35},
    {"Id":2,"Date":"2014-11-28","Amount":55}
]

我对 Ramda 很陌生,但我已经设法使用以下代码合并列表:

// Helper functions to build predicate list
var predicateListFunc = function (props) { return R.allPredicates(R.map(R.curry(R.eqProps), props)); }
var compareProperties = R.unapply(predicateListFunc);

// Function to merge lists based on object Ids and Dates
var mergeLists = R.unionWith(compareProperties("Id", "Date"));

// Function to sort in date descending order; used later to facilitate grouping
var sortByDateDesc = R.compose(R.reverse, R.sortBy(R.prop("Date")));

// Merge the lists
var mergedData = sortByDateDesc(mergeLists(listA, listB));

对于分组和求和:

// My original code used a side-effect because I could not get the R.reduce to 
// work.  Turns out it was a typo that prevented the initial list from propagating
// correctly.  I reimplemented it and spotted the typo after reading Furqan Zafar's 
// comment)
var groupCalc = function (list, item) {
    var index = R.findIndex(R.propEq("Id", item.Id), list);
    if (index >= 0) {
        list[index].Amount += item.Amount;
    } else 
        list.push(item); 

    return list;
};

var groupedList = R.reduce(groupCalc, [], mergedData);

虽然它看起来确实有效,但我想知道在 Ramda 中是否有更好的方法来解决这个问题?groupBy的文档表明它在这里没有用。

更新版本:jsFiddle

4

2 回答 2

4

这是一个使用 R.reduce 函数来避免副作用的小提琴:http: //jsfiddle.net/013kjv54/6/

我只用以下内容替换了您的分组代码:

var result = R.reduce(function(acc, tuple){
    acc.push({
        StockId: tuple[0],                
        Reference: R.maxBy(function(record){return new Date(record.Reference)}, tuple[1]).Reference,
        Amount: R.reduce(function(acc, record){return acc + record.Amount}, 0, tuple[1])
    });
    return acc;
}, [], R.toPairs(R.groupBy(function(record){return record.StockId})(mergedData)));
于 2014-11-30T16:08:41.793 回答
4

问这个问题时我没有看到这一点。如果您仍然对替代方法感兴趣,这里有一种不同的方法:

var combine = function(acc, entry) {
    return {
        Id: entry.Id, 
        Date: acc.Date && acc.Date > entry.Date ? acc.Date : entry.Date, 
        Amount: (acc.Amount || 0) + entry.Amount
    };
};

var process = R.pipe(
    R.groupBy(R.prop('Id')), 
    R.values, 
    R.map(R.uniqWith(R.eqProps('Date'))), 
    R.map(R.reduce(combine, {}))
);

var result = process(R.concat(listA, listB));

您可以在JSFiddle上看到它的实际效果。与许多此类方法一样,它存在一个潜在问题,即结果的顺序与底层 JS 引擎如何对其对象关键参数进行排序有关,尽管这在现代引擎中几乎是一致的。

于 2014-12-22T13:44:53.990 回答