比较三个非常大的数组并创建一个新对象

Posted

技术标签:

【中文标题】比较三个非常大的数组并创建一个新对象【英文标题】:Comparing three large very large arrays and creating a new object 【发布时间】:2021-10-07 01:29:51 【问题描述】:

好的,所以我在 node.js 中有三个数组,每个数组大约有 65k 个对象。它们都共享一个 ID - PARCELID、SBL 和 SBL20 是相同的。我想将来自不同数组的对象组合成一个对象,然后将其推入最终数组。无论出于何种原因,我得到的输出包含 130k+ 个对象。这也是非常低效的,所以如果有更好的方法可以做到这一点,我全神贯注 - 我尝试使用 map() 虽然我只能比较两个数组,而不是三个。我还有第四个我想添加到组合中。

var final = new Array();
  count=0
  TaxParcels.forEach((TaxParcel) => 
    TaxBills.forEach((TaxBill) => 
      if (TaxParcel.PARCELID == TaxBill.SBL20) 
        CodeEnforcements.forEach((CodeEnforcement) => 
          if (TaxParcel.PARCELID == CodeEnforcement.SBL) 
            parcel = 
              ID: TaxParcel.PARCELID,
              DETAILS: 
                TaxParcel: TaxParcel,
                TaxBill: TaxBill,
                CodeEnforcement: CodeEnforcement,
              ,
            ;
            final.push(parcel);
            count++
          
        );
      
    );
  );
  console.log(final);
  console.log(count)

【问题讨论】:

“无论出于何种原因,我得到的输出包含 130k+ 个对象。”:这意味着您在同一个输入数组中有重复的 ID 值。请提供样本(小的、虚拟的)输入,以及预期的输出。 可以确认没有重复的 ID 值。一旦我终于能够找到一个可以处理 200MB+ JSON 文件的 JSON 查看器,我发现它会将每个真实的语句推送到数组中四次。 如果您想对此进行解释,请展示一个可重现的案例(代码和数据)。 【参考方案1】:

您可以通过首先创建一个以键为键的 Map 来获得性能提升,其中每个对象都关联为空对象。然后将对象注入这些对象,并将结果过滤为只有具有所有 3 个键的对象:

// sample data
let TaxParcels = [ PARCELID: 3 ,  PARCELID: 9 ,  PARCELID: 7 ];
let TaxBills = [ SBL20: 9 ,  SBL20: 1 ,  SBL20: 3 ];
let CodeEnforcements = [ SBL: 3 ,  SBL: 9 ,  SBL: 1 ];

// Solution
let map = new Map([...TaxParcels, ...TaxBills, ...CodeEnforcements].map(o => 
    [o.PARCELID??o.SBL20??o.SBL, ])
);

for (let o of TaxParcels) map.get(o.PARCELID).TaxParcel = o;
for (let o of TaxBills) map.get(o.SBL20).TaxBill = o;
for (let o of CodeEnforcements) map.get(o.SBL).CodeEnforcement = o;

let result = [...map.values()].filter(o => Object.keys(o).length === 3);
console.log(result);
console.log(result.length);

【讨论】:

这样效率更高,也提供了正确的输出。回复:[o.PARCELID??o.SBL20??o.SBL, ]) - 问号在这种情况下是什么意思?另外,我以前从未见过使用“...”。如果我错了,请纠正我,但看起来 map 返回的数组格式在第一行格式化,然后我们将相关对象设置为 o? 另外,我以前从未见过“...”的用法。如果我错了,请纠正我,但看起来 map 返回的数组格式在第一行格式化,然后我们将相关对象设置为 o。 (抱歉,已编辑原始评论)。 ... 是 spread syntax。您可以改用concat。不知道你在说什么地图。 不会映射创建一个带有结果的新数组吗?所以我们用 [o.PARCELID??o.SBL20??o.SBL, ]) 格式化数组,然后我们用 for (let o of TaxParcels) map.get(o.PARCELID).TaxParcel 注入这些值= o; for (let o of TaxBills) map.get(o.SBL20).TaxBill = o; for (let o of CodeEnforcements) map.get(o.SBL).CodeEnforcement = o; 只是为了解释Map 构造函数:它接受一个参数,该参数必须是对数组(2 个数组)。所以就像[ [1, ], [2, ], [3, ] ]Map 构造函数将使用键 1、2 和 3(在此示例中)从该对象创建一个 Map 对象,并且每对中的值位于它旁边。 .map 调用正在创建这个对数组。【参考方案2】:

最简单(也许不是最重要)的方法是在这里避免forEach:即使在找到元素之后,它也会毫无意义地迭代整个数组。没有办法破解forEach。它可以用breakfind 数组方法替换为for-of 循环。这也将消除重复。最后一种方式示例:

const TaxParcels = [ PARCELID: 1,  PARCELID: 2,  PARCELID: 3];
const TaxBills = [ SBL20: 1,  SBL20: 2,  SBL20: 3];
const CodeEnforcements = [ SBL: 1,  SBL: 2,  SBL: 3];

const final = [];
let count=0;

for (const TaxParcel of TaxParcels) 
  const ID = TaxParcel.PARCELID;

  const TaxBill = TaxBills.find(( SBL20 ) => SBL20 === ID);
  const CodeEnforcement = CodeEnforcements.find(( SBL ) => SBL === ID);

  if (TaxBill && CodeEnforcement) 
    count++;
    final.push( ID, DETAILS:  TaxParcel, TaxBill, CodeEnforcement  );
  


console.log(count);
console.log(JSON.stringify(final, null, '  '));

但也许最重要的改进是使用更合适的 id-keyed 结构,就像这里的其他答案一样。

【讨论】:

这很好,但是从所选答案中获得的性能要好得多。感谢您的帮助!将来肯定会像这样组织 - 可能没有帮助,我已经拉了一个通宵......早上 6 点......需要睡觉。

以上是关于比较三个非常大的数组并创建一个新对象的主要内容,如果未能解决你的问题,请参考以下文章

比较 2 个数组并列出差异 - Swift

遍历 pandas 数据框中的行并匹配列表中的元组并创建一个新的 df 列

比较2个数组并列出差异 - Swift

遍历非常大的 JSON 数组

从 Sqlite 表中选择行的元组并有效地对元组进行排序

javascript根据另一个更大的数组创建具有固定长度的对象数组