Home > Enterprise >  Filter an array of objects based on another array and a condition
Filter an array of objects based on another array and a condition

Time:12-16

I am struggling to retrieve a subset from an array of objects based on specific conditions. I have an array of objects in the following format:

const messages = [
    {
        summary: '[x1fg66pwq-qft] Second reminder to submit supporting documents',
        date: '1624652200',
        type: 1
    },
    {
        summary: '[x1fg66pwq-fgh] Final reminder to submit supporting documents',
        date: '1629058600',
        type: 4
    },
    {
        summary: '[zy9l89ptb-yuw] Please submit your proof of address',
        date: '1631708200',
        type: 2
    },
    {
        summary: '[ggk9nygh8-pmn] Your application has been successful.',
        date: '1634300200',
        type: 1
    },
]

There is another array which provides the messages to retrieve based on the message id in square brackets of the summary:

const messageIds = ['x1fg66pwq', 'zy9l89ptb'];

The result should be retrieval of the latest messages based on what's in the messageIds array. The date field is in epoch.

const result = [
    {
        summary: '[x1fg66pwq] Final reminder to submit supporting documents',
        date: '1629058600',
        type: 4
    },
    {
        summary: '[zy9l89ptb] Please submit your proof of address',
        date: '1631708200',
        type: 2
    },
]

To achieve the above, I tried combining a filter and find which did not work for me:

const result = messages.filter((message) =>
        messageIds.find(id => message.summary.includes(testEvent))
    );

I would expect the above to return the first result in the array which has the summary specified. However, this always returns the full array for me without filtering. Could someone please help me achieve this?

CodePudding user response:

As you look for the latest, you could first create a map or plain object, keyed by the targeted message IDs, and then assign to each the message that has the greatest value for its date property:

const messages = [{summary: '[x1fg66pwq-qft] Second reminder to submit supporting documents',date: '1624652200',type: 1},{ summary: '[x1fg66pwq-fgh] Final reminder to submit supporting documents',date: '1629058600', type: 4},{ summary: '[zy9l89ptb-yuw] Please submit your proof of address',date: '1631708200',type: 2},{  summary: '[ggk9nygh8-pmn] Your application has been successful.', date: '1634300200',type: 1},];

const messageIds = ['x1fg66pwq', 'zy9l89ptb'];

// Create object keyed by the message ids:
let obj = Object.fromEntries(messageIds.map(id => [id, null]));
// Link the message to each id with minimal date
for (let msg of messages) {
    let id = msg.summary.match(/^\[(.*?)-/)?.[1];
    if (obj[id] === null ||  msg.date >  obj[id]?.date) obj[id] = msg;
}

let result = Object.values(obj);

console.log(result);

CodePudding user response:

The pieces of this are easy enough. It's just a matter of stringing them together:

const matches = (messages, ids) => ids .flatMap (
  id => messages .filter (({summary}) => summary .startsWith ('['   id))
                 .sort (({date: a}, {date: b}) => b - a)
                 .slice (0, 1)
)

const messages = [{summary: '[x1fg66pwq-qft] Second reminder to submit supporting documents', date: '1624652200', type: 1}, {summary: '[x1fg66pwq-fgh] Final reminder to submit supporting documents', date: '1629058600', type: 4}, {summary: '[zy9l89ptb-yuw] Please submit your proof of address', date: '1631708200', type: 2}, {summary: '[ggk9nygh8-pmn] Your application has been successful.', date: '1634300200', type: 1}]

const messageIds = ['x1fg66pwq', 'zy9l89ptb']

console .log (matches (messages, messageIds))

This version will simply ignore ids that aren't matched. That may or may not be what you want. It also does something that would be stupid if you have to search a large amount of data for each id: it finds the maximum by sorting them and choosing the first. For small data that's fine. For larger data (hundreds of thousands or millions is probably where it might first be a problem), this is O (n log (n)), which is slower than the following technique, which is O (n):

const maxBy = (fn) => (xs) => xs .reduce (
  ({curr, max}, x) => fn(x) > max ? {curr: x, max: fn (x)} : {curr, max}, 
  {curr: null, max: -Infinity}
) .curr

const matches = (messages, ids) => ids .flatMap (
  id => maxBy (({date}) => Number(date)) 
              (messages .filter (({summary}) => summary .startsWith ('['   id)))
)

const messages = [{summary: '[x1fg66pwq-qft] Second reminder to submit supporting documents', date: '1624652200', type: 1}, {summary: '[x1fg66pwq-fgh] Final reminder to submit supporting documents', date: '1629058600', type: 4}, {summary: '[zy9l89ptb-yuw] Please submit your proof of address', date: '1631708200', type: 2}, {summary: '[ggk9nygh8-pmn] Your application has been successful.', date: '1634300200', type: 1}]

const messageIds = ['x1fg66pwq', 'zy9l89ptb']

console .log (matches (messages, messageIds))

This uses a maxBy function which iterates through the list to find the maximum according to the supplied function, returning null for an empty array.

This version will then include null in the output in the case the id is not found. This might be the preferred behavior, but if not, you could wrap this to filter the output for non-null values.

Obviously, in either of these solutions, you could replace summary .startsWith ('[' id) with summary .includes (id), which is more flexible, but perhaps less exact.

CodePudding user response:

I think you just need to tack on a sort at the end if I am understanding your question correctly

.sort((b,a) => a.date - b.date)

const messages = [{
    summary: '[x1fg66pwq-qft] Second reminder to submit supporting documents',
    date: '1624652200',
    type: 1
  },
  {
    summary: '[x1fg66pwq-fgh] Final reminder to submit supporting documents',
    date: '1629058600',
    type: 4
  },
  {
    summary: '[zy9l89ptb-yuw] Please submit your proof of address',
    date: '1631708200',
    type: 2
  },
  {
    summary: '[ggk9nygh8-pmn] Your application has been successful.',
    date: '1634300200',
    type: 1
  },
]

const messageIds = ['x1fg66pwq', 'zy9l89ptb'];

const result = messages.filter((message) =>
  messageIds.some(id => message.summary.includes(id))
).sort((b, a) =>  a.date -  b.date);

console.log(result)

  • Related