I have a 2D array:
[ [ 1, 2, 43, 5 ],
[ 1, 5, 12, 1 ],
[ 2, 3, 6, 77 ],
[ 2, 1, 48, 94 ],
[ 3, 3, 15, 85 ],
[ 3, 7, 97, 86 ],
[ 4, 0, 9, 54 ],
[ 4, 1, 83, 16 ]]
This is the result I’m after:
[ [ 1, 7, 55, 6 ],
[ 2, 4, 54, 171 ],
[ 3, 10, 112, 171 ],
[ 4, 1, 92, 70 ]]
Match the first index within each array, and sum the other indexes.
My first thought was to use reduce and findIndex to find the index of the first item, add item to array if not found, otherwise sum the values.
But I really don’t know what I’m doing.
array.reduce((acc,e) =>
{ let index = acc.findIndex(inner => inner[0] === e[0]);
(index === -1) ? [...acc,e] : (acc[index][1] += e[1]) && (acc[index][2] += e[2]) && (acc[index][3] += e[3]);
return acc},[]);
Help please!
EDIT: I’m learning! A simple change to my attempt and it worked, thanks for pointing out my error @mykaf and @trincot.
Change made to update accumulator [...acc,e]
to acc.push(e)
const input = [ [ 1, 2, 43, 5 ],
[ 1, 5, 12, 1 ],
[ 2, 3, 6, 77 ],
[ 2, 1, 48, 94 ],
[ 3, 3, 15, 85 ],
[ 3, 7, 97, 86 ],
[ 4, 0, 9, 54 ],
[ 4, 1, 83, 16 ]];
const result = input.reduce((acc,e) =>
{ let index = acc.findIndex(inner => inner[0] === e[0]);
(index === -1) ? acc.push(e) : (acc[index][1] += e[1]) && (acc[index][2] += e[2]) && (acc[index][3] += e[3]);
return acc;
},[]);
console.log(result);
Read on for some excellent/better answers to the problem.
3
Answers
Using
reduce
is a good way to do it, but in your attempt you never update the accumulatoracc
: the callback always returns the same, unmutatedacc
array.I would however suggest using a
Map
or a plain object as your accumulator, so that you don’t need to scan an array withfindIndex
, but can immediately look up by key. Then when the reducing is complete, you can extract the values from the result.Here is how that would work:
If you want to optimize for speed, then you’re better off with plain old
for
loops, and to support any data type for the key column, you would use aMap
:This faster version mutates the input array. If you don’t want that, you’ll need to spend some processing time to create a new sub array by doing a spread like
acc.set(key, [...arr[i]]);
You can use vanilla reverse for-loop along with the function
Array.prototype.splice
to remove on-the-fly already processed array:The following approach it’s really fast and creates a copy of the original array to avoid mutation:
Since your data is sorted by the first index, you can just reduce and if a new index is encountered create new sum arrays otherwise add to the last array. If the input isn’t sorted, just sort: