Группировать по нескольким значениям Underscore.JS, но сохранить ключи и значения
Я пытаюсь сгруппировать следующий массив с объектами:
[ { 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 } ]
Как видите, он содержит комбинации user_id и alert_id, которые мне нравится группировать. Поэтому я хотел бы иметь следующий массив:
[ { user_id: 301, alert_id: 199, deals: [32243] },
{ user_id: 301, alert_id: 200, deals: [32243,107293,277470]}]
Кто-нибудь знает решение для этого? С подчеркиванием GroupBy я могу группировать значения на основе одного ключа. Но мне нужно сгруппировать их, основываясь на комбинации user_id AND alert_id, как вы можете видеть.
Я взглянул на underscore.nest, но проблема в том, что он создает свои собственные ключи.
Ответы
Ответ 1
Используйте 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')
}
});
Ответ 2
Я хотел бы добавить аккуратную группу 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;
}
Вам нужно будет включить параметр компилятора типа "downlevelIteration": true
чтобы позволить new Map
итерировать и возвращать values()
.
Ответ 3
Для какой-то конкретной задачи мне пришлось сохранить ключи и значения для нее
Так что я сделал этот вариант (с чистой 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;
}
Вот ссылка на суть:
groupify.js