Group by multiple values Underscore.JS but keep th

2019-01-11 16:47发布

问题:

I'm trying to group the following array with objects:

[ { 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 } ]

As you can see it contains user_id and alert_id combinations, which I like to group. So I would like to have the following array:

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

Anyone knows a solution for this? With underscore's GroupBy I can group the values based on one key. But I need to group them, based on the combination user_id AND alert_id, as you can see.

I took a look at underscore.nest, but the problem is it creates its own keys.

回答1:

Use groupBy with a function that creates a composite key using user_id and alert_id. Then map across the groupings to get what you want:

    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')
        }
    });


回答2:

I would like to contribute a neat typescript ES6 group by.
I am not really sure how to tighten up the return value typing. Some trickery is needed there, but this works rather well for me.

/**
 * 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;

}

You will need to turn on the typescript compiler option "downlevelIteration": true to allow the new Map to iterate and return values().