A fast (O(n)) and very readable approach
This is a pretty nice question, actually. I feel I can contribute to offer an answer that is more performant than previous answers (important if you're going to run this operation over and over) while also being very clear to read and understand.
You basically have a collection and want to reduce objects grouping them by a certain property.
Case 1: collection is sorted
If we can assume the collection is ordered by latest messages first (it looks like it), we can just retain the first ones we see:
const messages = [
{message: "ghhhhhhhh", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "14", time: "12:56"},
{message: "ggggggghjjgcgh", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:45"},
{message: "good afternoon", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:41"},
{message: "hfdsghfdfhjo", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:38"},
{message: "hhhhhhhhhhhhh ", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "14", time: "11:50"}
];
/** @type {Map<String, Object>} */
const latestMessageBySender = new Map();
// retain the most recent message from every unique sender
for (const message of messages) {
if (!latestMessageBySender.has(message.sender)) {
latestMessageBySender.set(message.sender, message);
}
}
// collect and show the resulting messages
for (const message of latestMessageBySender.values()) {
console.info(message);
}
Here we are using a Map
, which is the proper modern way of doing it (our intention is way clearer than using the old, hacky {}
for that).
Since latest messages come first, we just need to check if our map already contains that key. If it does, do nothing; otherwise, add the current message to the map.
Case 2: collection is not sorted
On the other hand, if it cannot be guaranteed that the collection is sorted, instead of having to parse the time field like other answers suggested, we can do it faster by just lexicographically comparing the strings:
if (message1.time > message2.time) {
// message 1 is more recent than message 2
}
We are considering here that the timestamp is well formed (2 digits for hours, 2 digits for minutes, i.e., /\d\d:\d\d/
). In other words, we expect 04:07
instead of 4:7
. We are also assuming that we only have messages from a same day, since there is no date given.
So, in that case, the final code would be:
const messages = [
{message: "ghhhhhhhh", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "14", time: "12:56"},
{message: "ggggggghjjgcgh", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:45"},
{message: "good afternoon", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:41"},
{message: "hfdsghfdfhjo", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:38"},
{message: "hhhhhhhhhhhhh ", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "14", time: "11:50"}
];
/** @type {Map<String, Object>} */
const latestMessageBySender = new Map();
// retain the most recent message from every unique sender
for (const message of messages) {
const previousMessage = latestMessageBySender.get(message.sender);
if (!previousMessage || message.time > previousMessage.time) {
latestMessageBySender.set(message.sender, message);
}
}
// collect and show the resulting messages
for (const message of latestMessageBySender.values()) {
console.info(message);
}
Before adding a message to the map, we first check if there is already one there for the same key. If there's not, just add the new one; if there is, lexicographically compare the time
fields and replace the existing one in case the new one is more recent.
An important to notice here that we do not want to actually sort the array first. That would be O(n log n)
, but we can get away with O(n)
by just comparing as you go.