JavaScript - "Combining" 两个相似的对象数组

JavaScript - "Combining" two similar arrays of objects

假设我有以下两个数组:

let arr1 = [ { id: "1234567890", name: "Someone", other: "unneeded", props: 123 } ... ];
let arr2 = [ { id: "1234567890", points: 100, other: "unneeded", props: 456 } ... ];

我需要根据名称和点按 id 组合这些,如下所示:

[ { id: "1234567890", name: "Someone", points: 100 } ... ]

一种选择是像这样映射它们:

let final = arr1.map(u => ({
    id: u.id,
    name: u.name,
    points: arr2.find(uu => uu.id === u.id)
}));

但是,这对于较大的数组(数千个条目)来说效率低下,因为 find() 每次都遍历数组。我正在努力提高效率。我阅读了 Array.reduce(),但这似乎不是答案(除非我错了)。我该怎么做?

可以从第二个数组的对象中创建一个Map,这样就可以在常数时间内直接通过id访问对应的对象:

let arr1 = [
  {id: 1234567890, name: "Someone", other: "unneeded", props: 123},
  {id: 1234567891, name: "Someone1", other: "unneeded1", props: 124},
  {id: 1234567892, name: "Someone2", other: "unneeded2", props: 125}
];
let arr2 = [
  {id: 1234567890, points: 100, other: "unneeded", props: 456},
  {id: 1234567891, points: 101, other: "unneeded", props: 457},
  {id: 1234567892, points: 102, other: "unneeded", props: 458}
];

let arr2Map = arr2.reduce((a, c) => {
  a.set(c.id, c);
  return a;
}, new Map());

let final = arr1.map(({id, name, points}) => 
  ({id, name, points: arr2Map.get(id).points || points}));

console.log(final);

您可以尝试的一种解决方案是 Webworkers。如果您以前没有使用过它们,它们可以 运行 在单独的线程中编写脚本,基本上允许您使用多个内核来处理某些事情。它们非常适合可并行化的任务,这意味着可以在没有任何中断的情况下分解的任务。它们非常适合您的用例,因为您只是在数据上绘制大地图。

在走这条路之前,你应该预先警告 webworkers 有一些开销。为了将数据放入 webworker 中,您必须将其序列化到线程中,然后在 returns 时将其反序列化。但是,如果您将任务拆分为单独的工作并让它们并行运行,您应该能够减轻其中的一些影响。

// app.js
let largeArray = []; // contains the large amount of arrays to map
let outputArray = []; 
while(largeArray.length){
    let worker = new Worker('/path/to/worker/script.js');
    worker.postMessage({
        chunk: largeArray.splice(0,1000)
    });
    worker.onmessage = evt => {
        let data = evt.data;
        outputArray = outputArray.concat(data);
        worker.terminate();
    }
}

另外有一个worker脚本,可能跟这个差不多,可以参考。

// workerScript.js
self.onmessage = ({data: {chunk}}) => {
    let final = chunk.map(u => ({
        id: u.id,
        name: u.name,
        points: arr2.find(uu => uu.id === u.id)
    }));
    self.postMessage(final);
}

您可能会问序列化,这是自动发生的事情。 Webworker 有自己的全局范围,作为序列化的结果,您可以发送对象、数组和原语。所有这些都可以序列化。具有自定义属性和 类 的对象会抛出错误。在幕后,它基本上是获取您的数据并执行 JSON.stringify({{your data}}),然后在 webworker 数据中成为 JSON.parse({{serialized data}}).

的结果

因为这项工作是在单独的线程中进行的,所以您不会在应用 运行ning 所在的主线程上看到任何阻塞。但是,如果您尝试一次处理太多,那么序列化会很明显,因为它在完成之前一直处于阻塞状态。