从数组中删除所有重复出现的元素

5

在人们认为这与Stack Overflow上关于删除重复项的所有其他答案相同之前,请查看返回内容与我想要做的内容有何不同。

我想要删除所有出现超过一次的元素。我尝试了以下方法:

const a = ['Ronaldo', 'Pele', 'Maradona', 'Messi', 'Pele'];

const uniqueArray = a.filter(function(item, pos) {
  return a.indexOf(item) == pos;
})

console.log(uniqueArray)

我希望我的uniqueArray是唯一的。
['Ronaldo', 'Maradona', 'Messi'];

抱歉,英文内容中并没有包含“Pele”这个词。请再次检查并提供正确的内容。 - peter flanagan
1
啊,好的,所以您想要删除所有出现超过一次的元素... - Jonas Wilms
是的,@JonasWilms 更新了问题,因为它可能不太清楚。 - peter flanagan
5个回答

12
简而言之:如果元素的第一次出现位置(indexOf)也是该元素在数组中的最后一个位置(lastIndexOf),则保留该值。
如果索引不相等,则该值重复,可以将其丢弃。

const a = ['Ronaldo', 'Pele', 'Maradona', 'Messi', 
           'Pele', 'Messi', 'Van Basten', 'Baggio', 
           'Messi', 'Seedorf', 'Maradona'];
    
const uniqueArray = a.filter(function(item) {
  return a.lastIndexOf(item) == a.indexOf(item);
});

console.log(uniqueArray);
/* output:  ["Ronaldo", "Van Basten", "Baggio", "Seedorf"] */

另一个想法是对数组进行迭代,将值作为对象的键添加,计算出现次数,然后保留出现总次数等于1的键。

const a = ['Ronaldo', 'Pele', 'Maradona', 'Messi', 
           'Pele', 'Messi', 'Van Basten', 'Baggio', 
           'Messi', 'Seedorf', 'Maradona'];
    
const obj = {};
const uniqueArray = [];

a.forEach((item) => {
  obj[item] = (typeof obj[item] === 'undefined')? 1 : ++obj[item];
});

for (let key in obj) {
  if (obj[key] === 1) {
    uniqueArray.push(key)
  }
}

console.log(uniqueArray);
/* output:  ["Ronaldo", "Van Basten", "Baggio", "Seedorf"] */

这段代码的复杂度是线性的,而第一个解决方案是二次的。
我在 JSPerf 上进行了 基准测试,使用由 535 个项目和 119 个唯一值组成的数组,最后的方法比第一个方法快了大约 11 倍。

1
我必须给你点赞,只是因为你加入了巴乔 :-D - peter flanagan
1
而且请考虑巴乔在他职业生涯的很大一部分时间里只用一条腿完成了他所做的一切。 - Fabrizio Calderan
请注意,对于较长的数组,这种方法效率非常低下(二次方数量级的比较),尽管在ES<6和混合类型的情况下很难做得更好。 - user202729
@user202729 发现了更好的方法。 - Fabrizio Calderan

3
您可以在每次迭代中计算项目数,以便将其包含在条件中:

const a = ['Ronaldo', 'Pele', 'Maradona', 'Messi', 'Pele'];

const uniqueArray = a.filter(function(item, pos) {
  var c = a.filter(i => i==item).length;
  return a.indexOf(item) == pos && c <= 1;
})

console.log(uniqueArray);

虽然更好的解决方案是将当前数组项的索引与Array.prototype.indexOf()Array.prototype.lastIndexOf()进行比较:

const a = ['Ronaldo', 'Pele', 'Maradona', 'Messi', 'Pele'];

const uniqueArray = a.filter(function(item, pos) {
  return a.indexOf(item) === a.lastIndexOf(item);
})

console.log(uniqueArray);


2
你可以使用 Map 来计算每个条目出现的次数,然后只选择那些仅出现一次的条目。

const a = ['Ronaldo', 'Pele', 'Maradona', 'Messi', 'Pele'];

const uniqueArray = onlyUniqueItems(a);

console.log(uniqueArray)


function onlyUniqueItems(array) {
  const map = new Map();
  const result = [];
  
  array.forEach(item => {
    let amount = map.get(item) || 0;
    map.set(item, ++amount); //pre-increment and then set the amount
  });
  
  map.forEach((amount, item) => {
    if (amount == 1) {
      result.push(item);
    }
  });
  
  return result;
}

这将以O(2n)的时间运行,因为你最多需要进行两次扫描。你可以使用普通对象并在其上设置键值来实现类似的结果,但是Map具有保留原始类型的优势,而普通对象会将所有键转换为字符串:

const a = [1, 2, 3, 3, "4", "4", "5"];

const uniqueArray = onlyUniqueItems(a);
const uniqueArrayUsingObject = onlyUniqueItemsUsingObject(a);

console.log(uniqueArray)
console.log(uniqueArrayUsingObject)


function onlyUniqueItems(array) {
  const map = new Map();
  const result = [];
  
  //count occurences
  array.forEach(item => {
    let amount = map.get(item) || 0;
    map.set(item, ++amount); //pre-increment and then set the amount
  });
  
  //extract only the items that show once
  map.forEach((amount, item) => {
    if (amount == 1) {
      result.push(item);
    }
  });
  
  return result;
}


function onlyUniqueItemsUsingObject(array) {
  const map = {};
  const result = [];
  
  //count occurences
  array.forEach(item => {
    let amount = map[item] || 0;
    map[item] = ++amount; //pre-increment and then set the amount
  });
  
  //extract only the items that show once
  Object.entries(map).forEach(([item, amount]) => {
    if (amount == 1) {
      result.push(item);
    }
  });
  
  return result;
}


1

天真的方法是检查是否存在多个出现:

   a.filter((el, i) => !(a.indexOf(el) !== i || a.indexOf(el, i) > -1)));

或者更复杂的多个集合(但O(n)):

  const found = new Set, values = new Set;

  for(const el of a)
   if(!found.has(el)) {
     found.add(el);
     values.add(el);
   } else if(values.has(el) {
     values.delete(el);
   }
 }

 const result = [...values.values()];

-1

为什么不使用Set

const theSet = new Set(a);
const values = set.values();
const uniqueArray = Array.from(values);

因为 OP 想要 完全 忽略重复值 - 如果一个值有一个副本,就不要包含它。 - VLAZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接