1

I have an array of objects like this:

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"}
];

And I want to obtain the most recent message of each sender, like this:

const messages = [
  {message: "ghhhhhhhh", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "14", time: "12:56"},
  {message: "ggggggghjjgcgh", receiver: "OX0pReHXfXUTq1XnOnTSX7moiGp2", sender: "ZCiuWczin3VuibH59MISuEqR3pc2", time: "12:45"}
];

How to do that?

Lucio Paiva
  • 19,015
  • 11
  • 82
  • 104
ch mariem
  • 67
  • 1
  • 6
  • Possible duplicate of [Get all unique values in a JavaScript array (remove duplicates)](https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates) – Mel Macaluso Nov 22 '19 at 11:05

4 Answers4

0

You can use array.reduce for this purpose, for example:

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"}];


function parseTime(timeStr) {
    const fields = timeStr.split(":").map(parseInt);
    return fields[0] * 60 + fields[1];
}

let result = messages.reduce( (map, item) => { 
   if (!map[item.sender] || parseTime(map[item.sender].time) < parseTime(item.time)) {
       map[item.sender] = item;
   } 
   return map;
}, {});

console.log("Latest messages:", Object.values(result));
Terry Lennox
  • 29,471
  • 5
  • 28
  • 40
0

You can do it like this:-

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"}]

const getUniqueMessages = (messages) => {
    const msgMap = {};
    const uniqueMsg = [];
    messages.forEach(item => {
        if(!msgMap[item.sender]) {
            msgMap[item.sender] = true;
            uniqueMsg.push(item)
        }
    });

   return uniqueMsg;   
}

// console.log(getUniqueMessages(messages));
Shivratna Kumar
  • 1,311
  • 1
  • 8
  • 18
0
const result = [];
const map = new Map();
for (const item of messages) {
    if(!map.has(item.sender)){
        map.set(item.sender, true);
        result.push({
            message: item.message,
    reciever: item.reciever
            sender: item.sender,
            time: item.time
        });
    }
}
console.log(result)
0

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.

Lucio Paiva
  • 19,015
  • 11
  • 82
  • 104