Array.prototype.unique = function() {
    var a = [], // uniques get placed into here
        b = 0; // counter to test if value is already in array 'a'

    for ( i = 0; i < this.length; i++ ) {
        var current = this[i]; // get a value from the original array

        for ( j = 0; j < a.length; j++ ) { // loop and check if value is in new array 
            if ( current != a[j] ) {
                b++; // if its not in the new array increase counter
            }
        }

        if ( b == a.length ) { // if the counter increased on all values 
                               // then its not in the new array yet
            a.push( current ); // put it in
        }

        b = 0; // reset counter
    }

    this.length = 0; // after new array is finished creating delete the original array
    for ( i = 0; i < a.length; i++ ) {
        this.push( a[i] ); // push all the new values into the original
    }

    return this; // return this to allow method chaining
}


我希望这是一个缓慢的检查器,尤其是因为没有首先进行排序。我对提高排序能力等感兴趣,所以我想我会得到较早的审查。

评论

与如何快速查找唯一列表项相关?

使用ES6的一行实现:Array.prototype.unique = function(){return [... new Set(this)]; }测试:[1,3,4,4,4,3,1,2,5,6,6,7] .unique()// [1、3、4、2、5、6、7]

#1 楼

您可以使用indexOf函数检查数组中是否存在值。这样可以大大简化您的代码:

Array.prototype.unique = function() {
    var a = [];
    for ( i = 0; i < this.length; i++ ) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }

    this.length = 0;
    for ( i = 0; i < a.length; i++ ) {
        this.push( a[i] );
    }

    return this;
}


最后,您要替换数组的内容。最好不要更改原始数组,而应使用唯一的元素返回新数组: a会更好。

应使用unique声明循环变量i,以将其范围限制为当前块,因此代码变为:

Array.prototype.unique = function() {
    var a = [];
    for ( i = 0; i < this.length; i++ ) {
        var current = this[i];
        if (a.indexOf(current) < 0) a.push(current);
    }
    return a;
}


最后,使用reduce函数可以更优雅地解决此问题:

Array.prototype.unique = function() {
    var unique = [];
    for (let i = 0; i < this.length; i++) {
        let current = this[i];
        if (unique.indexOf(current) < 0) unique.push(current);
    }
    return unique;
}


UPDATE(回答您的后续问题)

如果希望函数采用参数来决定是否应修改原始数组,则可以尝试如下操作:

Array.prototype.unique = function() {
    return this.reduce(function(accum, current) {
        if (accum.indexOf(current) < 0) {
            accum.push(current);
        }
        return accum;
    }, []);
}


评论


\ $ \ begingroup \ $
我做了一个小小的研究,发现几乎每个Javascript Array函数都永远不会改变原始数组……如果您不触摸原始数组,我认为它会更加连贯。
\ $ \ endgroup \ $
– Marco Acierno
2014年8月15日14:51

\ $ \ begingroup \ $
我会同意,如果我不是一个人工作,也不是100%肯定没有其他人会使用我的这种实现方式。所以我认为会没事的。 :D
\ $ \ endgroup \ $
–艾迪
2014年8月15日15:34



\ $ \ begingroup \ $
好答案。您的reduce可以更改为过滤器,因为这基本上就是它所做的。
\ $ \ endgroup \ $
–elclanrs
2014年8月15日在18:37

\ $ \ begingroup \ $
@MarcoAcierno:在我的头顶上,至少Array.pop(),Array.splice()和Array.unshift()改变了原始数组。
\ $ \ endgroup \ $
–hippietrail
16年6月18日在6:28

#2 楼

如果有一些重复项,则您的算法具有\ $ \ mathcal {O}(n ^ 2)\ $运行时。

如果Javascript具有广泛可用的Set

,不幸的是它还没有,那么人们可以像这样实现一个非常快速的实现:

Array.prototype.unique = function() {
    return [...(new Set(this))];
}


对于二叉树实现,这应该是\ $ \ mathcal {O}(n \ cdot \ log(n))\ $,或者是\ $ \ mathcal {O}(n)\ $用于基于哈希的实现。

排序和删除顺序

如果首先对数组进行排序(如建议的那样),则可以遍历数组并复制第一个元素,然后是与先前元素不同的所有剩余元素。该算法将具有\ $ \ mathcal {O}(n \ cdot \ log(n))\ $运行时。在这里,排序主要是运行时。对于小型列表,Janos的代码已足够。如果想要大型列表的性能,则确实需要排序。

此解决方案将不会保留元素的顺序,但对于大型列表将具有更快的运行时间:

Array.prototype.unique = function() {
    var sorted = this;
    sorted.sort();
    return sorted.filter(function(value, index, arr){
        if(index < 1) 
            return true;
        else
            return value != arr[index-1];
    });
}


评论


\ $ \ begingroup \ $
如果使用Set,则根本不需要for。 MDN页面上说,如果将迭代器对象传递给构造函数,则将添加它们,因此var seen = new Set(this);应该可以,因为Set不会添加重复的项目
\ $ \ endgroup \ $
– Marco Acierno
2014年8月15日14:54

\ $ \ begingroup \ $
哦,我什至没有注意到set保留了插入顺序。我已经习惯了Java和C ++并非如此。接得好!我无法测试,因为我现在没有时间或没有兼容的浏览器,但是根据MDN网站上的示例,该功能应该可以正常工作。
\ $ \ endgroup \ $
–艾米莉·L。
2014年8月15日14:57



\ $ \ begingroup \ $
它在Google Chrome Canary中不起作用,但是在Firefox中它可以正常工作。只需删除不合时宜的部分(它会创建一个String),[... mySet]会从Set中创建一个Array。 (i.imgur.com/GuwtZnT.png)。我们每天都需要花费很多时间才能使用它,但是很高兴在Javascript中看到它。 (嗯,它仍然是一种糟糕的语言。)
\ $ \ endgroup \ $
– Marco Acierno
2014年8月15日在15:14



#3 楼

这是一种利用对象也可以用作哈希图的事实的方法。 a的值除了确定唯一性外,不用于其他任何用途。

Array.prototype.unique = function() {
    var existing = {}, result = [], current;
    for ( i = 0; i < this.length; i++) {
        current = this[i];
        if(!existing.hasOwnProperty(current)) { 
           result.push(current);
           existing[current] = true; //any value will do
        }
    }
    return result;
}


这可以在\ $ \ mathcal {O}(n)\ $中使用,因为它可以可以在迭代原始数组的同时创建结果数组。基于排序的算法可能具有\ $ \ mathcal {O}(n \ cdot \ log(n))\ $。

但这并不一定意味着该实现会更快,因为在“ hash”中插入值的成本可能足够高,对于某些n来说,会使该实现变慢。优势在于简单性