按多个值分组,使用Underscore.JS但保留键和值。

37

我正在尝试对以下包含对象的数组进行分组:

[ { user_id: 301, alert_id: 199, deal_id: 32243 },
  { user_id: 301, alert_id: 200, deal_id: 32243 },
  { user_id: 301, alert_id: 200, deal_id: 107293 },
  { user_id: 301, alert_id: 200, deal_id: 277470 } ]

正如您所看到的,它包含用户 ID 和警报 ID 的组合,我希望将其分组。因此,我想要以下数组:

[ { user_id: 301, alert_id: 199, deals: [32243] },
  { user_id: 301, alert_id: 200, deals: [32243,107293,277470]}]

有人知道这个问题的解决方案吗?使用下划线的GroupBy,我可以根据一个键值对进行分组。但是我需要根据user_id和alert_id的组合进行分组,就像您所看到的那样。

我看了一下underscore.nest,但问题是它会创建自己的键。

3个回答

97

使用带有函数的groupBy,该函数使用user_id和alert_id创建一个组合键。然后在分组中进行映射以获得所需内容:

    var list = [ { user_id: 301, alert_id: 199, deal_id: 32243 },
      { user_id: 301, alert_id: 200, deal_id: 32243 },
      { user_id: 301, alert_id: 200, deal_id: 107293 },
      { user_id: 301, alert_id: 200, deal_id: 277470 } ];

    var groups = _.groupBy(list, function(value){
        return value.user_id + '#' + value.alert_id;
    });

    var data = _.map(groups, function(group){
        return {
            user_id: group[0].user_id,
            alert_id: group[0].alert_id,
            deals: _.pluck(group, 'deal_id')
        }
    });

问题已解决,谢谢。只是补充一下,我将键作为单独的字段返回,整个组作为一个字段返回,在我的angularjs重复器中运行得非常好。 - punkologist
2
我可以问一下井号(#)的用途是什么吗? - Konstantin Hadzhiev
5
它用于在alert_id和user_id之间加上分隔符,以便groupBy函数返回的字符串看起来像是'301#200'(Javascript会将数字强制转换为字符串)。如果不使用它,函数就会返回一个数字,例如501(301 + 200),这可能是不正确的,例如300 + 201 === 400 + 101。 - Gruff Bunny
如果两个键都是列表,例如deal_id: [32243, 3535, 242]和alert_id: [200,4242,242],需要根据这两个键的组合对所有项目进行分组,该怎么办?我卡在这里了,请帮忙。@GruffBunny - monica

0

我想贡献一个整洁的TypeScript ES6分组方法。
我不太确定如何收紧返回值类型。需要一些巧妙的技巧,但这对我而言效果相当不错。

/**
 * group the supplied list by a set of keys.
 * @param list the list of items to group.
 * @param children the key for the array of child items.
 * @param components the components to group by.
 */
groupByMultipleFields<T, K extends keyof T>(
    list: T[],
    children: string,
    ...components: K[]): any[] {

    var grouping = [...list.reduce((r, o) => {
        const key = components.map(_ => `${o[_]}`).join(" :: ");
        var keyed = r.get(key) || components.reduce((x, y) => { x[y] = o[y]; return x; }, <T>{});
        keyed[children] = keyed[children] || [];
        keyed[children].push(o);
        return r.set(key, keyed);
    }, new Map).values()];
    return grouping;

}

您需要打开 TypeScript 编译器选项 "downlevelIteration": true,以允许 new Map 迭代并返回 values()


0

为了完成某些特定任务,我需要安全地保存键和值。因此,我使用了这个变体(使用纯净的JS):

/**
 * Group your items in array by multiple keys!
 *
 * @param collection
 * @param retrieveFunctions
 * @param compareFunctions
 * @returns {Array}
 */
function groupify(
    collection,
    retrieveFunctions = {attribute: value => value},
    compareFunctions = {attribute: (groupValue, value) => groupValue === value}
) {
    let groups = [];
    let keyValues = {};

    collection.forEach(item => {
        for (let attribute in retrieveFunctions) {
            keyValues[attribute] = retrieveFunctions.hasOwnProperty(attribute) ? retrieveFunctions[attribute](item[attribute]) : undefined;
        }
        let group = groups.find(group => {
            for (let key in retrieveFunctions) {
                if (!group && !group.keys[key]) {
                    return false;
                }
                if (compareFunctions[key] instanceof Function) {
                    if (!compareFunctions[key](group.keys[key], keyValues[key])) {
                        return false;
                    }
                } else if (group.keys[key] !== keyValues[key]) {
                    return false;
                }
            }

            return true;
        });

        !group ? groups.push({keys: keyValues, items: [item]}) : group.items.push(item);
        keyValues = {};
    });

    return groups;
}

这里是gist上的链接: groupify.js


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接