简体   繁体   English

如何根据 object 值从两个 arrays 创建一个新的对象数组?

[英]How to create a new array of objects from two arrays based on object values?

I'm struggling with merging two arrays of objects (fetched from blockchain data) into a new array based on object values.我正在努力将两个 arrays 对象(从区块链数据中获取)合并到一个基于 object 值的新数组中。

The goal is to get the latest interaction with a user.目标是获得与用户的最新交互。

A simplified but close representation of the data structure this problem is faced in:这个问题面临的数据结构的简化但紧密的表示:

interface MsgSlice {
    messageId: string;
    messageDataSlice: {
        senderId?: string;
        receiverId: string;
        timestamp: number;
    };
};

const latestReceivedMsgs: MsgSlice[] = [
    {
        messageId: "messageId1",
        messageDataSlice: {
            senderId: "userId1",
            receiverId: "ownerId", // <- always same in that array
            timestamp: 101,
        },
    },
    {
        messageId: "messageId3",
        messageDataSlice: {
            senderId: "userId2",
            receiverId: "ownerId",
            timestamp: 103,
        },
    },
    {
        messageId: "messageId5",
        messageDataSlice: {
            senderId: "userId3",
            receiverId: "ownerId",
            timestamp: 105,
        },
    },
];

const latestSentMsgs: MsgSlice[] = [
    {
        messageId: "messageId2",
        messageDataSlice: {
            // senderId: "ownerId",
            receiverId: "userId1",
            timestamp: 102,
        },
    },
    {
        messageId: "messageId4",
        messageDataSlice: {
            receiverId: "userId3",
            timestamp: 104,
        },
    },
];

The desired result should contain the latest messageId either 'sent to' or 'received by' the corresponding user.所需结果应包含“发送给”或“由相应用户接收”的最新 messageId。 Something like this:像这样的东西:


const latestInteraction = [
    {
        user: "userId1",
        messageId: "messageId2",
        timestamp: 102,
    },
    {
        user: "userId2",
        messageId: "messageId3",
        timestamp: 103,
    },
    {
        user: "userId3",
        messageId: "messageId5",
        timestamp: 105,
    },
]   

As a solution I thought of looping over the arrays and per iteration also looping over the other array to compare the senderId and receiverId values.作为一种解决方案,我考虑循环遍历 arrays 并且每次迭代还循环遍历另一个数组以比较senderIdreceiverId值。 If " senderId is == one of the looped receiverId s", it could be sent into an interaction array and then time sorted and filtered.如果“ senderId is == 循环的receiverId之一”,则可以将其发送到interaction数组中,然后进行时间排序和过滤。 Unfortunately, I couldn't figure out how to get it working.不幸的是,我不知道如何让它工作。 Also, I thought that my thinking might be limited here and that there are probably more efficient ways to do it than in my solution concept.另外,我认为我的想法可能在这里受到限制,并且可能有比我的解决方案概念更有效的方法来做到这一点。

The approach I'd take is to convert your received and sent messages into a single array of "interactions" that contain only the information you care about.我采取的方法是将您接收和发送的消息转换为一个“交互”数组,其中仅包含您关心的信息。 For a received message you want to look at the senderId , whereas for a sent message you want to look at the receiverId (the idea is that you want the other user for each interaction, not the current user).对于收到的消息,您希望查看senderId ,而对于发送的消息,您希望查看receiverId (想法是您希望每个交互的其他用户,而不是当前用户)。 That could look like this:这可能看起来像这样:

interface Interaction {
  user: string
  messageId: string
  timestamp: number
}

function latestInteractions(
  receivedMsgs: MsgSlice[], 
  sentMsgs: MsgSlice[]
): Interaction[] {

  const allInteractions: Interaction[] = [];
  for (const m of receivedMsgs) {
    const sender = m.messageDataSlice.senderId;
    if (sender === undefined) continue;
    allInteractions.push({
      user: sender,
      messageId: m.messageId,
      timestamp: m.messageDataSlice.timestamp
    });
  }
  for (const m of sentMsgs) {
    allInteractions.push({
      user: m.messageDataSlice.receiverId,
      messageId: m.messageId,
      timestamp: m.messageDataSlice.timestamp
    });
  }

Note that if somehow a received message doesn't have a senderId then we just skip it.请注意,如果以某种方式接收到的消息没有senderId ,那么我们就跳过它。 Maybe we should throw an error instead?也许我们应该抛出一个错误? That's up to you.这取决于你。 Now we have a single array filled with all interactions.现在我们有一个包含所有交互的数组。 We want to collect just one such interaction for each user in the array, and if we ever have more than one we should keep just the one with the greatest timestamp .我们只想为数组中的每个user收集一个这样的交互,如果我们有多个交互,我们应该只保留timestamp最大的那个。 That could look like this:这可能看起来像这样:

  const interactionMap: { [k: string]: Interaction } = {};
  for (const i of allInteractions) {
    if (!(i.user in interactionMap) || interactionMap[i.user].timestamp < i.timestamp) {
      interactionMap[i.user] = i;
    }
  }

The interactionMap is now a plain object whose keys are the user strings and whose values are the latest Interaction for each user.现在, interactionMap是一个普通的 object,其键是user字符串,其值是每个用户的最新Interaction This has all the information we want, but you want an array and not an object.这包含我们想要的所有信息,但您需要一个数组而不是 object。 So we can just use the Object.values() method to get an array of the values:所以我们可以只使用Object.values()方法来获取值的数组:

  return Object.values(interactionMap);
}

That's an array in some order;那是按某种顺序排列的数组; if you care you can sort it according to your needs.如果您在乎,可以根据需要对其进行sort


Let's make sure it works with your example:让我们确保它适用于您的示例:

const latestInteraction = latestInteractions(latestReceivedMsgs, latestSentMsgs);
console.log(latestInteraction);
/* [{
  "user": "userId1",
  "messageId": "messageId2",
  "timestamp": 102
}, {
  "user": "userId2",
  "messageId": "messageId3",
  "timestamp": 103
}, {
  "user": "userId3",
  "messageId": "messageId5",
  "timestamp": 105
}]  */

Looks good!看起来不错!

Playground link to code Playground 代码链接

You can use the hash grouping approach, the vanila JS solution可以使用hash分组方式,vanila JS解决方案

Live Demo:现场演示:

 const latestReceivedMsgs = [{messageId: "messageId1",messageDataSlice: {senderId: "userId1",receiverId: "ownerId", timestamp: 101,},},{messageId: "messageId3",messageDataSlice: {senderId: "userId2",receiverId: "ownerId",timestamp: 103,},},{messageId: "messageId5",messageDataSlice: {senderId: "userId3",receiverId: "ownerId",timestamp: 105,},},]; const latestSentMsgs = [{messageId: "messageId2",messageDataSlice: {receiverId: "userId1",timestamp: 102,},},{messageId: "messageId4",messageDataSlice: {receiverId: "userId3",timestamp: 104,},},]; const grouped = [...latestReceivedMsgs, ...latestSentMsgs].reduce((acc, { messageId, messageDataSlice }) => { const { timestamp, senderId, receiverId } = messageDataSlice; const user = senderId?? receiverId; const msgItem = { user, messageId, timestamp }; if ((acc[user]?.timestamp?? 0) < timestamp) acc[user] = msgItem; return acc; }, {}); const result = Object.values(grouped); console.log(result);
 .as-console-wrapper { max-height: 100%;important: top: 0 }

UPDATE更新

Or typescript variant:或 typescript 变体:

interface MsgSlice {
  messageId: string;
  messageDataSlice: {
    senderId?: string;
    receiverId?: string;
    timestamp: number;
  };
};

interface Interaction {
  user: string
  messageId: string
  timestamp: number
};

const latestReceivedMsgs: MsgSlice[] = [{messageId: "messageId1",messageDataSlice: {senderId: "userId1",receiverId: "ownerId", // <- always same in that array},},{messageId: "messageId3",messageDataSlice: {senderId: "userId2",receiverId: "ownerId",timestamp: 103,},},{messageId: "messageId5",messageDataSlice: {senderId: "userId3",receiverId: "ownerId",timestamp: 105,},},];
const latestSentMsgs: MsgSlice[] = [{messageId: "messageId2",messageDataSlice: {receiverId: "userId1",timestamp: 102,},},{messageId: "messageId4",messageDataSlice: {receiverId: "userId3",timestamp: 104,},},];

const grouped = ([...latestReceivedMsgs, ...latestSentMsgs] as MsgSlice[])
  .reduce((acc, { messageId, messageDataSlice }) => {
    const { timestamp, senderId, receiverId } = messageDataSlice;
    const user = senderId ?? receiverId ?? "unindefined";
    const msgItem = { user, messageId, timestamp };
    if ((acc[user]?.timestamp ?? 0) < timestamp) acc[user] = msgItem
    
    return acc;
  }, {} as { [key: Interaction['user']]: Interaction });

const result: Interaction[] = Object.values(grouped);

console.log(result);

You could simply flatten both arrays into one, then sort them per timestamp.您可以简单地将 arrays 合并为一个,然后按时间戳对它们进行排序。 Such as:如:

let msgs: MsgSlice[] = [];
msgs.push(...latestReceivedMsgs);
msgs.push(...latestSentMsgs);

msgs.sort((a, b) => {
    return a.timestamp - b.timestamp ;
});

Perhaps you can join them into a single array and then sort them by their timestamp?也许您可以将它们加入一个数组,然后按它们的时间戳对它们进行排序?

const sortedMsgs = [...latestReceivedMsgs, ...latestSentMsgs] sortedMsgs.sort((a,b)=>a.messageDataSlice.timestamp-b.messageDataSlice.timestamp) const sortedMsgs = [...latestReceivedMsgs, ...latestSentMsgs] sortedMsgs.sort((a,b)=>a.messageDataSlice.timestamp-b.messageDataSlice.timestamp)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何根据两个现有 arrays 的值创建新的对象数组? - How to create a new array of objects based on the values of two existing arrays? 基于 Javascript 中的两个 arrays 创建新的对象数组 - Create new array of objects based on two arrays in Javascript Underscorejs:如何通过组合两个对象的值来创建新对象? - Underscorejs:How to create a new object by combining values from two objects? 通过将两个数组与对象进行比较来创建一个新的布尔值数组 - Create a new array of boolean values by comparing two arrays with objects 迭代两个对象数组并创建新的数组对象 - Iterate over two arrays of objects and create new array object 如何基于对象数组创建新对象 - How to create a new Object based on an array of objects 创建基于两个不同数组的新数组(匹配值) - Create new array based of two different arrays (match values) 我想根据相似对象数组中的键动态创建对象值数组 - I want to dynamically create arrays of object values based on their keys from an array of similar objects 从包含对象的嵌套数组数组中创建新对象 - Create new object from array of nested arrays that contains objects 使用JavaScript使用两个数组中的不同值创建对象数组 - Create array of objects using distinct values from two arrays with javascript
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM