Chat) Array.prototype.reduce()
This post was migrated from Tistory. You can find the original here.
While building a chat application, I wanted a user’s profile picture to show only on the first message of a consecutive run of chats from that same user.
For a run of consecutive messages, there should be one profile picture shared across multiple chat bubbles.
So I needed to transform something like:
[ responseMsg, responseMsg, responseMsg ... ]
into a grouped form like:
[[ responseMsg, responseMsg ], [responseMsg, responseMsg, responseMsg ...]]
The idea is that if the sender of the previous message matches the sender of the next one, they should end up in the same sub-array.
Since this requires remembering the state at the previous index, reduce (as an accumulator) came to mind.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
useEffect(() => {
if (msgArr.length != 0) {
const resArr: responseMsgDto[][] = [];
let cacheArr: responseMsgDto[] = [];
if (msgArr.length == 1) resArr.push(msgArr); // fix
msgArr.reduce((acc, cur, idx) => {
if (cacheArr.length == 0) cacheArr = [acc];
if (acc.userId != cur.userId) {
resArr.push(cacheArr);
cacheArr = [];
}
cacheArr.push(cur);
if (idx == msgArr.length - 1) {
resArr.push(cacheArr);
}
return cur;
});
// console.log(resArr);
setReMsgArr(resArr);
}
}, [msgArr])
I considered producing the result directly from reduce’s return value, but that looked overly complicated, so I just pulled resArr out as an external array instead.
In the middle of the iteration: if the user is the same, keep pushing onto cacheArr; if the user differs, push cacheArr into resArr and reset cacheArr to empty.
At the very start, cacheArr doesn’t yet contain the first element, so it’s initialized as [ acc ].
At the very end, whatever is left accumulated in cacheArr gets pushed into resArr.
Fix)
If the array’s length is 1, reduce never runs the callback at all — it just returns the accumulator value directly and finishes.