Is there a way to efficiently implement a group by function without mutation?
Naive implementation:
- ReScript playground
- JavaScript (see below)
var messages = [
{insertedAt: "2021-01-10"},
{insertedAt: "2021-01-12"},
{insertedAt: "2021-01-13"},
{insertedAt: "2021-01-13"},
{insertedAt: "2021-01-13"},
{insertedAt: "2021-01-14"},
{insertedAt: "2021-01-15"},
{insertedAt: "2021-01-15"},
{insertedAt: "2021-01-16"},
{insertedAt: "2021-01-17"},
{insertedAt: "2021-01-17"},
{insertedAt: "2021-01-17"},
{insertedAt: "2021-01-18"},
{insertedAt: "2021-01-18"},
]
var messagesGroupedByDate = messages.reduce(function (data, message) {
if (
data.some(function (point) {
return point.date === message.insertedAt;
})
) {
return data.map(function (point) {
if (point.date === message.insertedAt) {
return {
date: point.date,
count: (point.count + 1) | 0,
};
} else {
return point;
}
});
} else {
return data.concat([
{
date: message.insertedAt,
count: 1,
},
]);
}
}, []);
console.log(messagesGroupedByDate);
For the sake of argument, there's no need to make this more generic. The problem I'm facing is that I'm looping three times:
- once with
Array.prototype.reduce
which is necessary to loop overmessages
- once with
Array.prototype.some
to see if the date key already exists in the resulting array - in the case where a date key already exists, we loop again with
Array.prototype.map
to update a specific element of an array - otherwise, a new array is returned that contains the new element
If there's not really any good way to make this efficient in ReScript, then I can always use raw JavaScript for this function, but I'm curious if it's possible to do this efficiently without mutation.