Как склеить элементы массива в один, суммировав некоторое значение, при определенных условиях в js?
Есть массив, где каждый элемент тоже массив:
let arr = [
['orange', '2021-07-01', 100],
['apple', '2021-07-01', 100],
['orange', '2021-07-01', 400],
['apple', '2021-07-06', 100],
['apple', '2021-07-01', 400],
];
Необходимо все подмассивы, где совпадают первые два значения, склеить в один, суммировав их третье значение.
То есть, на выходе должен получиться массив где склеены подмассивы с индексами 0 и 2, а также 1 и 4:
[
['orange', '2021-07-01', 500],
['apple', '2021-07-01', 500],
['apple', '2021-07-06', 100],
]
Ответы (2 шт):
Можно сначала посчитать суммы в хэштаблицу (где ключами является объединение некоторых значений) - а затем преобразовать ее обратно в массив (с разбором каждого ключа обратно в значения). Итого два прохода. А точнее, один проход по данным, и один по хэштаблице:
let arr = [
['orange', '2021-07-01', 100],
['apple', '2021-07-01', 100],
['orange', '2021-07-01', 400],
['apple', '2021-07-06', 100],
['apple', '2021-07-01', 400],
];
const merged = arr.reduce((rslt, [fruit, date, value]) => {
const key = [fruit, date].join('|');
rslt[key] = (rslt[key] || 0) + value;
return rslt;
}, Object.create(null));
const reconstructed = Object.entries(merged).map(
([key, value]) => [...key.split('|'), value]
);
console.log(reconstructed);
Разделителем в ключе может быть любая подстрока, которая заведомо не будет встречаться в ключевых значениях.
Тот же самый подход может быть применен с любым (≥2) количеством ключевых значений, с массивами объектов (а не только с массивом подмассивов), и т.д..
Нужен ключ из пары значений. К сожалению, JavaScript допускает в качестве ключей только строки и числа. Даже Map не поможет - он принимает массивы в качестве ключей но сравнивает их не по значению.
JSON.stringify преобразует массив в строку гарантируя что разные по значению массивы породят разные строки. В словаре хранятся целиком записи (фрукт/дата/значение), не только значения. Фрукт и дату можно было бы восстановить из ключа, я так не делаю, чтобы не добавлять ещё одну сложную операцию:
const merge = a => {
const map = Object.create(null);
for (const [fruit, date, value] of a) {
const key = JSON.stringify([fruit, date]);
const record = map[key];
if (record === undefined) {
map[key] = [fruit, date, value];
} else {
record[2] += value
}
}
return Object.values(map);
};
console.log(merge([
['orange', '2021-07-01', 100],
['apple', '2021-07-01', 100],
['orange', '2021-07-01', 400],
['apple', '2021-07-06', 100],
['apple', '2021-07-01', 400],
]));