从数组中删除重复的值 - JavaScript/jQuery

12

我这里有一个数组:

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];

现在我想要移除重复的两个出现。所以期望的结果不是

var myArr = [1, 2, 5, 7, 8 ,9];

但是

var myArr = [2, 7, 8];

我基本知道如何删除重复项,但不知道那种特殊的方法。因此,任何帮助都将不胜感激!

请注意:我的数组中填充了字符串。这里使用的数字仅作为示例。


3
它是否总是按顺序排列和/或为数字? - jeschafe
一个值是否可能出现超过两次? - j08691
@jeschafe:那有什么关系呢?反正你也无法在次线性时间内完成...如果这是你想要的。 - Razvan
@jeschafe 它充满了字符串,没有顺序。j08691:不,没有任何值会出现超过两次。 - Sven
仅仅因为它会影响代码的编写方式。下面的一些函数不能处理数组中的字符串。 - jeschafe
JSPref 比较 http://jsperf.com/deleting-both-values-from-array - Matt Urtnowski
9个回答

1

更好的答案已编辑:

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];

function removeDuplicates(arr) {
    var i, tmp;
    for(i=0; i<arr.length; i++) {
        tmp = arr.lastIndexOf(arr[i]);
        if(tmp === i) {
            //Only one of this number
        } else {
            //More than one
            arr.splice(tmp, 1);
            arr.splice(i, 1);
        }
    }
}

我本来要用这个策略来回答。这是我写的jsFiddle帮助链接:http://jsfiddle.net/VBYun/ - Jesse
最终,myArr 包含 [1, 5, 7, 9] - j08691
唉,我刚意识到这只适用于数组中只有两个数字的情况。如果那是最大值,有一种更简单的方法可以检查lastIndexOf()与当前i值是否匹配。如果它们匹配,那就没问题了,如果不匹配,你可以根据每个索引删除两个数字。 - Joel Fischer
1
var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];function removeDuplicates(arr) { var i, tmp; for(i=0; i<arr.length; i++) { tmp = arr.lastIndexOf(arr[i]); if(tmp === i) { //只有一个此数字 } else { //不止一个 arr.splice(tmp, 1); arr.splice(i, 1); } } }console.log(removeDuplicates(myArr)); - Joel Fischer
我喜欢这个答案...但是我认为如果元素不按顺序排列,它可能不起作用:http://jsfiddle.net/vBcmp/ - Richard JP Le Guen
...而且OP已经确认该数组是“填充了字符串,没有排序”。 - Richard JP Le Guen

1

请使用jsfiddle查看此代码:

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
var newArr = myArr;
var h,i,j;


for(h = 0; h < myArr.length; h++) {
    var curItem = myArr[h];
    var foundCount = 0;
    // search array for item
    for(i = 0; i < myArr.length; i++) {
        if (myArr[i] == myArr[h])
            foundCount++;
    }
    if(foundCount > 1) {
        // remove repeated item from new array
        for(j = 0; j < newArr.length; j++) {
            if(newArr[j] == curItem) {                
                newArr.splice(j, 1);
                j--;
            }
        }            
    }
}

能否在Lodash中实现? - achu

1
无论在哪里涉及到去重,使用set数据结构都不是一个坏主意。
JavaScript没有原生的set实现,但对象的键同样有效——在这种情况下有所帮助,因为值可以用来跟踪项目在数组中出现的次数:

function removeDuplicates(arr) {
  var counts = arr.reduce(function(counts, item) {
    counts[item] = (counts[item] || 0) + 1;
    return counts;
  }, {});
  return Object.keys(counts).reduce(function(arr, item) {
    if (counts[item] === 1) {
      arr.push(item);
    }
    return arr;
  }, []);
}

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
console.log(removeDuplicates(myArr), myArr);

在jsfiddle上查看示例

或者,您可以不使用reduce()调用,而是使用forfor(item in counts)循环:

function removeDuplicates(arr) {
    var counts = {};
    for(var i=0; i<arr.length; i++) {
        var item = arr[i];
        counts[item] = (counts[item]||0)+1;
    }
    var arr = [];
    for(item in counts) {
        if(counts[item] === 1) {
            arr.push(item);
        }
    }
    return arr;
}

在 jsfiddle 上查看示例


1
虽然我很喜欢使用.reduce,但我怀疑在counts对象上进行简单的迭代会更有效率... - Alnitak
@Alnitak - 你是指像 for item in counts { /*...*/ } 这样的东西吗? - Richard JP Le Guen
1
是的,这就是我的意思。使用.reduce每次迭代调用函数的开销可能会非常高。 - Alnitak
如果您查看我的答案中的 jspref,您会发现它们的性能并不好。 - Matt Urtnowski
现在Javascript确实有本地的Set实现:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set - connexo
你的方法对于包含除字符串以外的任何内容的数组都不起作用(例如对象、函数等),因为尝试将该值设置为对象键将调用该对象的 toString() 方法。请参见你的示例,它会将原始数组中的数字转换为字符串。使用映射而不是对象。 - connexo

1

这是我的版本

var a = [1, 1, 2, 5, 5, 7, 8, 9, 9];

function removeIfduplicate( arr ) {
    var discarded = [];
    var good      = [];
    var test;
    while( test = arr.pop() ) {
        if( arr.indexOf( test ) > -1 ) {
            discarded.push( test );
            continue;
        } else if( discarded.indexOf( test ) == -1 ) {
            good.push( test );
        }
    }
    return good.reverse();
}

x = removeIfduplicate( a );
console.log( x ); //[2, 7, 8]

1

使用Hashmap

  1. 创建Hashmap并计算出现次数
  2. 筛选出 hashmap.get(value) === 1 的内容(仅唯一值)
const myArray = [1, 1, 2, 5, 5, 7, 8, 9, 9];
const map = new Map();
myArray.forEach(v => map.set(v, map.has(v) ? map.get(v)+1 : 1));
myArray.filter(v => map.get(v) === 1);

旧版本(速度较慢,但也有效)

这里有一个使用Array.filter()的简短版本。诀窍是先找到所有不唯一的值,然后使用此数组拒绝原始数组中的所有唯一项。

let myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
let duplicateValues = myArr.filter((item, indx, s) => s.indexOf(item) !== indx);
myArr.filter(item => !duplicateValues.includes(item));
// => [2, 7, 8]

0
如果只涉及字母数字,重复项区分大小写,每个元素最多不能超过两个,那么可以使用类似如下的代码:
var a = [2, 1, "a", 3, 2, "A", "b", 5, 6, 6, "B", "a"],

    clean_array = $.map(a.sort(), function (v,i) {
        a[i] === a[i+1] && (a[i] = a[i+1] = null);
        return a[i];
    });

// clean_array = [1,3,5,"A","B","b"]

0
在这个例子中,我们将两个数组作为函数参数,从中仅打印出唯一的值,因此删除同时出现在两个数组中的值。
首先,我将两个数组连接成一个。 然后,我逐个检查每个数组值,并循环遍历数组本身以搜索其出现次数。 如果出现次数(即计数)等于1,则将该元素推送到结果数组中。 然后我们可以返回结果数组。
function diffArray(arr1, arr2) {
  var newArr = [];
  var myArr=arr1.concat(arr2);
  var count=0;
  for(i=0;i<myArr.length;i++){
    for(j=0;j<myArr.length;j++){
      if(myArr[j]==myArr[i]){
        count++;
      }
    }
    if(count==1){
      newArr.push(myArr[i]);
    }
    count=0;
  }
  return newArr;
}

请提供更多的解释。 - Thomas Rollet

-1

编辑:这里是 jsperf http://jsperf.com/deleting-both-values-from-array

http://jsfiddle.net/3u7FK/1/

这是最快的方法,使用两个步骤而不使用任何花哨的技巧并保持灵活性。首先遍历并找到每个出现次数的计数,并将其放入键值对中。然后再次遍历它,并过滤掉计数大于1的那些。这也具有能够应用其他过滤器而不仅仅是“大于1”的优点;以及如果您需要其他东西的出现次数。

这也适用于字符串而不仅仅是数字。

http://jsfiddle.net/mvBY4/1/

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
var map = new Object();

for(var i = 0; i < myArr.length; i++)
{
    if(map[myArr[i]] === undefined)
    {
        map[myArr[i]] = 1;
    }
    else
    {
        map[myArr[i]]++;
    }
}

var result = new Array();

for(var i = 0; i < myArr.length; i++)
{   
    if(map[myArr[i]] > 1)
    {
        //do nothing
    }
    else
    {
        result.push(myArr[i]);
    }

}

alert(result);

如果你追求性能和速度,第二个循环比必要的迭代次数更多。像@Alnitak建议的那样,你应该使用for(key in map) { /*...*/ }循环。 - Richard JP Le Guen

-1
您可以按照以下方式使用 Set(仅适用于 IE 11+)。
const sourceArray = [1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8];
const duplicatesRemoved = new Set();

sourceArray.forEach(element => {
    if (duplicatesRemoved.has(element)) {
        duplicatesRemoved.delete(element)
    } else {
        duplicatesRemoved.add(element)
    }
})

console.log(Array.from(duplicatesRemoved))

注意:箭头函数不支持旧版浏览器。请改用普通函数语法。但是,Array.from 可以轻松地为旧版浏览器提供 polyfill

在这里尝试一下


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