Is there a way to efficiently implement a group by function without mutation?
Naive implementation:
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:
Array.prototype.reduce
which is necessary to loop over messages
Array.prototype.some
to see if the date key already exists in the resulting arrayArray.prototype.map
to update a specific element of an arrayIf 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.
Just add data to a Map()
and then convert to array, then to object. It doesn't mutate anything as per your request.
We may simplify this even more but it is 5:00 AM right now and my brain is asleep now.
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"},
];
const mapped = new Map();
messages.forEach(message => {
// if date already seen before, increment the count
if (mapped.has(message.insertedAt)) {
const count = mapped.get(message.insertedAt);
mapped.set(message.insertedAt, count+1);
} else {
// date never seen before, add to map with initial count
mapped.set(message.insertedAt, 1);
}
});
const msgArr = Array.from(mapped);
const final = msgArr.map(([date, count])=> ({date, count}));
console.log(final);