根据另一个数组和条件过滤一组对象

Posted

技术标签:

【中文标题】根据另一个数组和条件过滤一组对象【英文标题】:Filter an array of objects based on another array and a condition 【发布时间】:2022-01-19 00:19:51 【问题描述】:

我正在努力根据特定条件从一组对象中检索一个子集。我有以下格式的对象数组:

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
    ,
]

还有另一个数组,它根据摘要方括号中的消息 ID 提供要检索的消息:

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

结果应该是根据messageIds 数组中的内容检索最新消息。日期字段以纪元为单位。

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
    ,
]

为了实现上述目的,我尝试组合一个过滤器并找到对我不起作用的:

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

我希望上面返回数组中指定摘要的第一个结果。但是,这总是为我返回完整的数组而不过滤。有人可以帮我实现这一目标吗?

【问题讨论】:

我认为您的方法没有问题。它返回过滤后的消息数组,其中包含在您的messageIds 中指定的相同摘要。我尝试执行它,并得到一个包含 3 个项目而不是 4 个项目的新数组。现在您只需要获取最新消息。我错了吗? 是的,我只需要获取最新的,我认为这是我的方法不起作用的地方 @Julien 绝对。代码按预期工作。他实际上有两个具有相同 ID 的摘要。他想要最新的。 你的做法没有错。你只需要一步一步地做事。在这里,您已经过滤了您的数组。现在您可以按 ID 对其进行分组,然后按日期对每个组进行排序,然后从每个组中取出第一项。这样您就会收到最新消息。 抱歉,我在消息对象中遗漏了一些内容。方括号中的字符串是 id 后跟一个具有潜在含义的三个字母的单词。 messagesId 数组只包含 id 而不是三个字母的单词。帖子已更新 【参考方案1】:

如果我正确理解您的问题,我认为您只需要在最后添加sort

.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)

【讨论】:

为什么find ...? @NinaScholz - 谢谢。我切换到findIndex >-1 以获得过滤器的布尔值。 为什么不改用some?它返回一个布尔值。 @NinaScholz - 谢谢【参考方案2】:

当您寻找最新的时,您可以首先创建一个映射或普通对象,以目标消息 ID 为键,然后将其@987654321 具有最大价值的每个消息分配给@属性:

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);

【讨论】:

【参考方案3】:

这很容易。只需将它们串在一起即可:

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))

此版本将简单地忽略不匹配的 id。这可能是也可能不是你想要的。如果您必须为每个 id 搜索大量数据,它也会做一些愚蠢的事情:它通过对它们进行排序并选择第一个来找到最大值。对于小数据,这很好。对于较大的数据(可能首先出现问题的可能是数十万或数百万),这是O (n log (n)),它比以下技术慢,即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))

这使用了一个maxBy 函数,它遍历列表以根据提供的函数找到最大值,返回一个空数组的null

如果找不到 id,此版本将在输出中包含 null。这可能是首选行为,但如果不是,您可以包装它以过滤非空值的输出。

显然,在这两种解决方案中,您都可以将summary .startsWith ('[' + id) 替换为summary .includes (id),这样更灵活,但可能不太精确。

【讨论】:

以上是关于根据另一个数组和条件过滤一组对象的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery LEFT JOIN 一个表并根据条件过滤其数组元素

javascript 根据选择条件过滤数组对象。

如何过滤复杂对象的列表,以便如果两个具有字段值,我会根据条件删除一个

在Javascript中按多个条件过滤数组

如何根据使用角度 7 中的键的条件检查来过滤 json 响应中的数组

js过滤数组中都为空的对象几种方式