【问题标题】:Eliminate duplicates of several arrays消除多个数组的重复项
【发布时间】:2017-06-02 14:20:39
【问题描述】:

我有 3 个数组:

array1 = [ 'A', 'B', 'A', 'B']
array2 = [   5,   5,   7,   5]
array3 = [true,true,true,true]

我想知道是否有任何简单的方法(可能使用 lodash)来消除重复并以此结束:

array1 = [ 'A', 'B', 'A']
array2 = [   5,   5,   7]
array3 = [true,true,true]

我知道我可以做一个函数并比较之前的值,但是有没有更聪明的方法呢?

更新 请注意,我不需要消除每个数组的重复项。 我正在寻找的是一种“垂直”消除重复项的方法

更新 2 请注意,每个“列”都是一个记录。 record1 = ['A',5,true] record2 = ['B',5,true] record3 = ['A',7,true] record1 = ['B',5,true]

【问题讨论】:

  • 你的第二个例子仍然有重复... --- 最简单的方法是使用 ES2015 中的[...new Set(array1)]
  • 您只是从输入数组中删除最后一个元素..
  • 实际上,使用 Lodash 非常简单。你可以使用_.difference(array, [values])。参考here.
  • 如果我理解正确的话,每个“列”都是一个“记录”,您要消除重复记录吗?
  • 您是在寻找就地解决方案(从给定数组中删除重复项),还是在寻找生成新过滤数组的不可变解决方案?

标签: javascript


【解决方案1】:

TL;DR

const records = array1.map((a, i) => [a, array2[i], array3[i]]);

const index = {};
records.filter(column => {
  const key = JSON.stringify(column);
  return key in index ? false : index[key] = true;
});

嗯?

有很多方法可以解决这个问题,效率各不相同,最佳解决方案取决于数据的大小。一个简单但幼稚的解决方案遍历每个“列”并检查所有前面的列是否相等。它看起来像这样:

const array1 = [ 'A', 'B', 'A', 'B'];
const array2 = [   5,   5,   7,   5];
const array3 = [true,true,true,true];

const newArray1 = array1.slice(0,1); // column 0 is never duplicate
const newArray2 = array2.slice(0,1);
const newArray3 = array3.slice(0,1);

// loop over columns starting with index 1
outer: for (let i = 1; i < array1.length; i++) {
  const a = array1[i];
  const b = array2[i];
  const c = array3[i];
  
  // check all preceding columns for equality
  for (let j = 0; j < i; j++) {
    if (a === array1[j] && b === array2[j] && c === array3[j]) {
      // duplicate; continue at top of outer loop
      continue outer;
    }
  }

  // not a duplicate; add to new arrays
  newArray1.push(a);
  newArray2.push(b);
  newArray3.push(c);
}

console.log(newArray1);
console.log(newArray2);
console.log(newArray3);
.as-console-wrapper{min-height:100%}

如您所见,我们每次都必须检查每列中的每一行是否相等。如果你好奇的话,它的复杂性是?(?(?+1)/2)(技术上是?(??(?+1)/2),其中?是三列的3)。

对于较大的数据集,跟踪您已经在可快速访问的数据结构中看到的值是有利的:哈希,也称为 JavaScript 对象。由于您的所有值都是原始值,因此构造键的快速方法是JSON.stringify。有些人可能会认为这是一种“hack”——重要的是要注意它会因无法用 JSON 表示的值而失败,例如InfinityNaN——但它的数据如此简单,既快速又简单。

const array1 = ['A', 'B', 'A', 'B'];
const array2 = [5, 5, 7, 5];
const array3 = [true, true, true, true];

const newArray1 = [];
const newArray2 = [];
const newArray3 = [];

const index = {};

for (let i = 0; i < array1.length; i++) {
  const a = array1[i];
  const b = array2[i];
  const c = array3[i];
  const key = JSON.stringify([a,b,c]);
  
  if (key in index) {
    // duplicate; skip to top of loop
    continue;
  }
  
  // not a duplicate; record in index and add to new arrays
  index[key] = true;
  newArray1.push(a);
  newArray2.push(b);
  newArray3.push(c);
}

console.log(newArray1);
console.log(newArray2);
console.log(newArray3);
.as-console-wrapper{min-height:100%}

它的复杂性是?(?),或者可能是?(2??),其中?,再次, 三列是 3,而 2 是另一个 ?,非常粗略地说明了 JSON.stringify 的成本。 (弄清楚哈希访问的成本留给我们中间的学究们练习;我很乐意称之为 ?(1)。)

这仍然很冗长。部分原因是对数据使用三个不同的变量(实际上是一个“表”)会导致大量重复。我们可以对数据进行预处理,使其更易于处理。一旦它被“转置”成单个二维数组,我们可以使用Array.prototype.filter 和上面的关键技术,以获得一些非常简洁的代码:

const array1 = ['A', 'B', 'A', 'B'];
const array2 = [5, 5, 7, 5];
const array3 = [true, true, true, true];

// turn "columns" into "rows" of a 2D array
const records = array1.map((a, i) => [a, array2[i], array3[i]]);

const index = {};
const newData = records.filter(column => {
  const key = JSON.stringify(column);
  return key in index ? false : index[key] = true;
});

console.log(newData);
.as-console-wrapper{min-height:100%}

当然,预处理不是免费的,所以这段代码的性能并不比更冗长的版本高;你必须决定这对你有多重要。如果您愿意,现在可以将 newData 中的列提取到三个变量 (newData.forEach(([a,b,c]) =&gt; { newArray1.push(a); newArray2.push(b); /* ... */ })) 中,但对于许多目的,“转置”二维数组将更易于使用。

【讨论】:

  • 最佳答案正是我阅读问题时的想法。不过,我喜欢展示不同的外观。
【解决方案2】:

你可以使用 ES6 设置https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

Set -> 允许您存储任何类型的唯一值,无论是原始值还是对象引用。

然后转回数组

检查这个sn-p

const array1 = ['A','B','A','B']
const array2 = [5,5,7,5]
const array3 = [true,true,true,true]

const uniqA1= new Set(array1)
const uniqA2= new Set(array2)
const uniqA3= new Set(array3)

console.log(Array.from(uniqA1))
console.log(Array.from(uniqA2))
console.log(Array.from(uniqA3))

希望对你有帮助

【讨论】:

  • 与 OP 的示例性输出不匹配 - 我承认这似乎违反直觉。
  • 这也不是很好的支持。
【解决方案3】:

您需要在所有数组中找到具有相同索引的重复元素,然后过滤掉这些元素。

var array1 = ['A', 'B', 'A', 'B'],
  array2 = [5, 5, 7, 5],
  array3 = [true, true, true, true];

var dupes = []
var arrays = [array1, array2, array3];

arrays.forEach(function(arr, i) {
  arr.forEach((e, j) => !this[e] ? this[e] = true : dupes[i] = (dupes[i] || []).concat(j))
}, {})

var index = dupes[0].filter(e => dupes.every(a => a.includes(e)))
var result = arrays.map(e => e.filter((a, i) => !index.includes(i)))

console.log(result)

【讨论】:

  • 嗯,这实际上对我有用。但是还有其他更简单的解决方案吗?
  • @Coyolero 这个答案并没有消除每个“列”都是“记录”的“重复记录”——正如您在上面的 cmets 中所确认的那样。请澄清。
【解决方案4】:

您将需要几个辅助函数(lodash 也提供了它们):

let zip = (...arys) => arys[0].map((_, i) => arys.map(a => a[i]));
let uniq = (ary, key) => uniq2(ary, ary.map(key), new Set);
let uniq2 = (ary, keys, set) => ary.filter((_, i) => !set.has(keys[i]) && set.add(keys[i]))

// test

var array1 = ['A', 'B', 'A', 'B'];
var array2 = [5, 5, 7, 5];
var array3 = [true, true, true, true];

var [x, y, z] = zip(
    ...uniq(
        zip(array1, array2, array3),
        JSON.stringify
    )
);

console.log(x, y, z)

【讨论】:

  • 使用JSON.stringify“散列”记录的有趣方法,但当数组包含例如NaNInfinitySymbol()...您可能想将其添加到描述中。
  • @le_m:当然,我的意思是介绍一些有用的泛型。
【解决方案5】:

另一种方式filter():

array1 = ['A', 'B', 'A', 'B'];
array2 = [5, 5, 7, 5];
array3 = [true, true, true, true];

uniqueArray1 = array1.filter(function(item, pos) {
  return array1.indexOf(item) == pos;
})

uniqueArray2 = array2.filter(function(item, pos) {
  return array2.indexOf(item) == pos;
})

uniqueArray3 = array3.filter(function(item, pos) {
  return array3.indexOf(item) == pos;
})

console.log(uniqueArray1);
console.log(uniqueArray2);
console.log(uniqueArray3);

【讨论】:

    【解决方案6】:

    我能想到的一种方法是使用一个对象来跟踪,这也会巧合地删除任何重复项,因为键必须是唯一的。唯一的事情是我现在可以想到如何将其提取回数组中。我明天再考虑。

    这利用 jquery 进行深度克隆。如果您只希望在 vanilla javascript 中使用它,您可能只需要实现一个深度克隆功能。

    var array1 = [ 'A', 'B', 'A', 'B'];
    var array2 = [   5,   5,   7,   5];
    var array3 = [true,true,true,true];
    
    all_arrays = [array1, array2, array3];
    
    let obj = {};
    for (let i = 0; i < all_arrays[0].length; i++)
    {
    	let new_obj = recursive_objects(all_arrays, 0, i)
      $.extend(true, obj, new_obj);
    }
    
    console.log(obj);
    
    function return_array(array, temp_obj)
    {
    	let keys = Object.keys(temp_obj);
      for (let key of keys)
      {
      	
      }
    }
    
    
    function recursive_objects(arrays, arrays_index, index)
    {
      let obj = {}
    	
      if (arrays_index < arrays.length)
      {
        obj[arrays[arrays_index][index]] = recursive_objects(arrays, ++arrays_index, index);  
      }
    
      return obj;
    }
    &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

    【讨论】:

      猜你喜欢
      • 2019-04-08
      • 1970-01-01
      • 2015-06-24
      • 1970-01-01
      • 1970-01-01
      • 2020-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多