Javascript удаляет дубликаты в списке объектов, объединяя некоторые свойства

У меня есть этот список:

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"
   }
];

Кто-нибудь может мне помочь? Спасибо

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
1 034
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Используйте функцию 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);

Выглядит отлично, почему вы использовали a[key] = Object.assign({},c) вместо a[key] = c?

SergioP 19.11.2018 12:41

@SergioP - Объекты передаются по ссылке и всякий раз, когда a[key].amount += c.amount; изменяется, объект. Я хотел избежать перезаписи объекта в массиве

Nikhil Aggarwal 19.11.2018 12:44
Ответ принят как подходящий

Если вас интересует решение с использованием 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. Сгруппируйте должников и кредиторов вместе
  2. Для каждой группы должников / кредиторов объедините сумму
  3. Извлечь каждую группу должников / кредиторов в массив

Шаг 1: groupBy (groupKey)

Шаг 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: карта (уменьшить (mergeAmount, {}))

Шаг 2 получает результат с шага 1

{
    "Mark ~> John": {
        amount: 30,
        creditor: "John",
        debtor: "Mark"
    },
    "Mark ~> Tom": {
        amount: 10,
        creditor: "Tom",
        debtor: "Mark"
    }
}

Шаг 3: ценности

Шаг 3 получает результат с шага 2

[
    {
        amount: 30,
        creditor: "John",
        debtor: "Mark"
    },
    {
        amount: 10,
        creditor: "Tom",
        debtor: "Mark"
    }
]

Чуть чище, пожалуй, для второй ступени был бы map(reduce(mergeAmount, {amount: 0})).

Scott Sauyet 19.11.2018 15:30

Интересно, что мое собственное решение было почти идентичным, за исключением комментария выше и встраивания того, что вы называете groupKey. И, что очень интересно, я инвертирую порядок ваших последних двух шагов; это работает в любом случае. Я вообще-то думаю, что Рамде нужна функция вроде (fn) => (list) => values(groupBy(fn, list)). Это такое обычное сочетание.

Scott Sauyet 19.11.2018 15:34

Я согласен, это чище. Не уверен, о чем я думал, но в converge не было необходимости. Все, что я сэкономил, - это всего лишь одна итерация за счет удобочитаемости. Спасибо!

customcommander 19.11.2018 15:40

Вы можете преобразовать map(reduce(mergeAmount, {amount: 0})) в map(reduce(mergeAmount, {})), потому что, если ключ не существует в обоих, mergeWithKey не использует обратный вызов.

Ori Drori 19.11.2018 15:45

Другие вопросы по теме