У меня есть этот список:
const debts = [
{
amount: 10,
debtor: "Mark",
creditor: "John"
},
{
amount: 20,
debtor: "Mark",
creditor: "John"
},
{
amount: 10,
debtor: "Mark",
creditor: "Tom"
}
];
... и я хочу объединить элементы с одним и тем же должником и кредитором, вычисляя общую сумму, например
const debts = [
{
amount: 30, // 10 + 20
debtor: "Mark",
creditor: "John"
},
{
amount: 10,
debtor: "Mark",
creditor: "Tom"
}
];
Кто-нибудь может мне помочь? Спасибо



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Используйте функцию filter(), чтобы просмотреть результирующий список и проверить, существует ли уже пара должник-кредитор. Если он существует, то обновляет сумму. В противном случае добавьте его как новую запись в результирующий список.
const debts = [
{
amount: 10,
debtor: "Mark",
creditor: "John"
},
{
amount: 20,
debtor: "Mark",
creditor: "John"
},
{
amount: 10,
debtor: "Mark",
creditor: "Tom"
}
];
var result = []
debts.forEach((item) => {
var existing = result.filter((resultItem, index) => {
return resultItem.debtor === item.debtor && resultItem.creditor === item.creditor;
});
if (existing.length) {
var existingIndex = result.indexOf(existing[0]);
result[existingIndex].amount += item.amount;
} else {
result.push(item);
}
})
console.info(result)Вы можете использовать Array.reduce для создания объекта с ключом в качестве уникальной пары должник и кредитор и значением в качестве результирующего объекта. В случае, если для пары в объекте уже существует запись, увеличьте сумму, иначе добавьте новую запись в объекте.
Наконец, используйте Object.values, чтобы собрать все объекты.
let debts = [{amount: 10,debtor: "Mark",creditor: "John"},{amount: 20,debtor: "Mark",creditor: "John"},{amount: 10,debtor: "Mark",creditor: "Tom"}];
let result = Object.values(debts.reduce((a,c) => {
let key = `${c.debtor}~~${c.creditor}`;
if (a[key]) a[key].amount += c.amount;
else a[key] = Object.assign({},c);
return a;
}, {}));
console.info(result);@SergioP - Объекты передаются по ссылке и всякий раз, когда a[key].amount += c.amount; изменяется, объект. Я хотел избежать перезаписи объекта в массиве
Если вас интересует решение с использованием Ramda, вот предложение:
const {
pipe,
mergeWithKey,
map,
reduce,
values,
groupBy,
props,
join
} = R;
const debts = [{
amount: 10,
debtor: "Mark",
creditor: "John"
},
{
amount: 20,
debtor: "Mark",
creditor: "John"
},
{
amount: 10,
debtor: "Mark",
creditor: "Tom"
}
];
const mergeAmount = mergeWithKey((key, left, right) => key === 'amount' ? left + right : left);
const groupKey = pipe(props(['debtor', 'creditor']), join(' ~> '));
const process =
pipe(
groupBy(groupKey),
map(reduce(mergeAmount, {})),
values);
console.info(process(debts));<script src = "https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>Идея состоит в том, чтобы разделить процесс на три этапа:
Шаг 1 получает исходный массив debts
{
"Mark ~> John": [
{
amount: 10,
creditor: "John",
debtor: "Mark"
},
{
amount: 20,
creditor: "John",
debtor: "Mark"
}
],
"Mark ~> Tom": [
{
amount: 10,
creditor: "Tom",
debtor: "Mark"
}
]
}
Шаг 2 получает результат с шага 1
{
"Mark ~> John": {
amount: 30,
creditor: "John",
debtor: "Mark"
},
"Mark ~> Tom": {
amount: 10,
creditor: "Tom",
debtor: "Mark"
}
}
Шаг 3 получает результат с шага 2
[
{
amount: 30,
creditor: "John",
debtor: "Mark"
},
{
amount: 10,
creditor: "Tom",
debtor: "Mark"
}
]
Чуть чище, пожалуй, для второй ступени был бы map(reduce(mergeAmount, {amount: 0})).
Интересно, что мое собственное решение было почти идентичным, за исключением комментария выше и встраивания того, что вы называете groupKey. И, что очень интересно, я инвертирую порядок ваших последних двух шагов; это работает в любом случае. Я вообще-то думаю, что Рамде нужна функция вроде (fn) => (list) => values(groupBy(fn, list)). Это такое обычное сочетание.
Я согласен, это чище. Не уверен, о чем я думал, но в converge не было необходимости. Все, что я сэкономил, - это всего лишь одна итерация за счет удобочитаемости. Спасибо!
Вы можете преобразовать map(reduce(mergeAmount, {amount: 0})) в map(reduce(mergeAmount, {})), потому что, если ключ не существует в обоих, mergeWithKey не использует обратный вызов.
Выглядит отлично, почему вы использовали
a[key] = Object.assign({},c)вместоa[key] = c?