按值A对2个数组进行重复数据删除并按值B排序的最有效方法?

时间:2019-10-19 14:08:40

标签: javascript arrays sorting merge

我有一个数组,其中包含具有标识符和排序值的对象。当我正在查询的端点返回带有更新对象的数组时,我需要将其与现有数组合并,传入数组的重复标识符对象优先于现有数组,但它们要彼此并排。例如

arrayA = [A:1, B:4, C:6]

arrayB = [D:2, A:3, C:5, G:7]

result = [D:2, A:3, B:4, C:5, G:7]

到目前为止,我想不出任何解决方案,其中不包括我首先对数组进行重复数据删除然后对它们进行排序,这对于我要使用的长列表而言似乎效率很低。解决这个问题的最有效方法是什么?

1 个答案:

答案 0 :(得分:1)

您可以执行mergesort使用的常规合并步骤,但可以跳过arrayA中已经在arrayB中的元素。要快速执行此检查,您可以先将arrayB中的所有ID添加到Set中。这将导致最终运行时间为 O(n + m)(为Set提供额外的空间),而不是 O((n + m)log(n + m )

function linearMerge(A, B) {
  const res = [];
  const newIds = new Set(B.map(o => o.id));
  A = A.filter(o => !newIds.has(o.id));

  let i = 0, j = 0;
  while (i < A.length && j < B.length) {
    if (A[i].val < B[j].val) res.push(A[i++]);
    else res.push(B[j++]);
  }
  while (i < A.length) res.push(A[i++]);
  while (j < B.length) res.push(B[j++]);
  return res;
}

const arrayA = [{id: 'A', val: 1}, {id: 'B', val: 4}, {id: 'C', val: 6}];
const arrayB = [{id: 'D', val: 2}, {id: 'A', val: 3}, {id: 'C', val: 5}, {id: 'G', val: 7}];

const result = linearMerge(arrayA, arrayB);
console.log(result); // [D:2, A:3, B:4, C:5, G:7]

当然,您的原始方法也可以使用,但是如果您使用Set来删除重复项,则可以对其进行改进:

function nLogNMerge(A, B) {
  const newIds = new Set(B.map(o => o.id));
  return [...A.filter(o => !newIds.has(o.id)), ...B]
    .sort((a, b) => a.val - b.val);
}

const arrayA = [{id: 'A', val: 1}, {id: 'B', val: 4}, {id: 'C', val: 6}];
const arrayB = [{id: 'D', val: 2}, {id: 'A', val: 3}, {id: 'C', val: 5}, {id: 'G', val: 7}];

const result = nLogNMerge(arrayA, arrayB);
console.log(result); // [D:2, A:3, B:4, C:5, G:7]


我测试了两种方法,其中arrayAarrayB有4500个条目,并且线性合并的确优于第二种方法(快“约20%”)。您可以在这里找到测试:https://jsperf.com/merge-sorted-arrays-with-duplicates/1