Группировка и фильтрация массива по нескольким признакам JS

Дан некий массив объектов:

    const arr = [
  {
    method: 1,
    currency: 'RUB',
    amount: 10,
    priority: 1,
  },
  {
    id: 22,
    method: 1,
    currency: 'RUB',
    amount: 100,
    priority: 1,
  },
  {
    id: 25,
    method: 1,
    currency: 'RUB',
    amount: 49,
    priority: 1,
  },
  {
    id: 33,
    method: 1,
    currency: 'USD',
    amount: 55,
    priority: 1,
  },
  {
    id: 77,
    method: 1,
    currency: 'USD',
    amount: 47,
    priority: 2,
  },
  {
    id: 30,
    method: 1,
    currency: 'USD',
    amount: 57,
    priority: 2,
  },
  {
    id: 7,
    method: 5,
    currency: 'RUB',
    amount: 140,
    priority: 1,
  },
  {
    id: 37,
    method: 5,
    currency: 'RUB',
    amount: 22,
    priority: 1,
  },
  {
    id: 5,
    method: 3,
    currency: 'RUB',
    amount: 100,
    priority: 1,
  },
]

Массив может быть сколь угодно большим. Стоит задача выбрать из него только "актуальные" элементы. То есть, нужно сначала сгруппировать элементы по method, currency, priority, а затем в каждой группе забрать только те элементы, у которых amount > 50 + элемент группы, у которого amount < 50, но самый большой в группе. Нужные элементы вынести в отдельный массив.

Иными словами нужно сначала определить элементы с одинаковым методом, валютой и приоритетом и из них забрать только те, у которых сумма больше 50 и элемент с суммой меньше 50, но больше всех остальных в этой группе.

По массиву из примера результат должен быть следующим:

    result = [
  {
    id: 22,
    method: 1,
    currency: 'RUB',
    amount: 100,
    priority: 1,
  },
  {
    id: 25,
    method: 1,
    currency: 'RUB',
    amount: 49,
    priority: 1,
  },
  {
    id: 33,
    method: 1,
    currency: 'USD',
    amount: 55,
    priority: 1,
  },
  {
    id: 30,
    method: 1,
    currency: 'USD',
    amount: 57,
    priority: 2,
  },
  {
    id: 77,
    method: 1,
    currency: 'USD',
    amount: 47,
    priority: 2,
  },
  {
    id: 7,
    method: 5,
    currency: 'RUB',
    amount: 140,
    priority: 1,
  },
  {
    id: 37,
    method: 5,
    currency: 'RUB',
    amount: 22,
    priority: 1,
  },
  {
    id: 5,
    method: 3,
    currency: 'RUB',
    amount: 100,
    priority: 1,
  },
]

Не могу ещё в такую сложную (для меня) группировку элементов массива, прошу помощи. Мне нужен именно алгоритм такой фильтрации. Заранее спасибо!


Ответы (1 шт):

Автор решения: EzioMercer

Основная идея - это группировать по единому ключу, который является объединением нужных для группировки св-в и уникальным для каждой группы, остальное элементарно:

  • Все у кого amount > 50 автоматом попадают в конечный массив
  • Далее по одному смотрим не тех у кого amount <= 50. Если он первый то запоминаем его, если не первый то сравниваем с тем что уже хранится, если у нового amount больше чем у сохранённого то делаем замену иначе просто пропускаем

Вот реализация:

const arr = [{
    method: 1,
    currency: 'RUB',
    amount: 10,
    priority: 1,
  },
  {
    id: 22,
    method: 1,
    currency: 'RUB',
    amount: 100,
    priority: 1,
  },
  {
    id: 25,
    method: 1,
    currency: 'RUB',
    amount: 49,
    priority: 1,
  },
  {
    id: 33,
    method: 1,
    currency: 'USD',
    amount: 55,
    priority: 1,
  },
  {
    id: 77,
    method: 1,
    currency: 'USD',
    amount: 47,
    priority: 2,
  },
  {
    id: 30,
    method: 1,
    currency: 'USD',
    amount: 57,
    priority: 2,
  },
  {
    id: 7,
    method: 5,
    currency: 'RUB',
    amount: 140,
    priority: 1,
  },
  {
    id: 37,
    method: 5,
    currency: 'RUB',
    amount: 22,
    priority: 1,
  },
  {
    id: 5,
    method: 3,
    currency: 'RUB',
    amount: 100,
    priority: 1,
  },
];

const groups = new Map();
const filteredArr = [];

arr.forEach(item => {
  const isAmountBig = item.amount > 50;

  if (isAmountBig) {
    filteredArr.push(item);
    return;
  }

  const key = `${item.method}-${item.currency}-${item.priority}`;

  if (!groups.has(key)) {
    groups.set(key, {maxValue: item.amount, item});
    return;
  }

  const group = groups.get(key);

  if (item.amount <= group.maxValue) return;

  group.maxValue = item.amount;
  group.item = item;
});

[...groups.values()].forEach(groupItem => filteredArr.push(groupItem.item));

console.log(filteredArr);

P.S. При желании new Map() можно спокойно заменить обычным объектом, логика не пострадает, только надо будет переписать пару строк

→ Ссылка