0

澄清:

正如一些人指出的那样,这看起来确实像“这段代码可以吗”之类的问题。我真正好奇的主要事情是:该方法是如何.hasOwnProperty工作的?
我的意思是:IE 的 JScript 引擎(至少<9)并不总是使用哈希表,所以我认为它只是迭代该对象的所有属性,然后检查它是否从另一个对象获取这些属性原型链更高。这是一个公平的假设吗?毕竟:在某种程度上,每个代码都被翻译成循环和分支,但是如果 IE 不做哈希表,那不意味着.hasOwnProperty只是一些糖,所以你不必编写循环吗?
我想我是从 DC 的一篇博客文章或视频中得到这个概念的,很可能他在谈论数组,以及它们在 JS 中是(或者更确切地说:可以是)的古怪事物。我找不到视频/博客帖子(?)自动取款机。鉴于 JS 数组经常被滥用,我认为你们中的许多人都会同意,我认为这个问题的答案可以作为一个不错的参考。这就是为什么我没有在codereview上发布它。

就目前的答案而言,由于我的问题是从错误的角度开始的(更关注代码而不是其背后的机制),所以让我老生常谈,感谢大家指出我没有指出的问题考虑到。


最近,我为我正在编写的一个脚本扩充了数组原型(暂时不要拍摄)。为了不重新发明轮子,我去搜索了一些其他人如何做的例子。

Strangly这是很常见的,这显然是不必要的复杂。作为替代方案,我也发现了这一点,除了发帖者对他的快速算法相当沾沾自喜之外,我仍然觉得还有改进的余地。

我知道我现在可能会成为一个自鸣得意的聪明人,我的unique方法实际上是这样的:

Array.prototype.unique = function()
{
    'use strict';
    var i,obj,ret;
    ret = [];
    obj = {};
    for (i=0;i<this.length;i++)
    {
        if (!obj.hasOwnProperty(this[i]))
        {
            ret.push(this[i]);
            obj[this[i]] = i;
        }
    }
    return ret;
};

这样,就不需要第二个循环 AFAIK,是吗?当然,除非该hasOwnProperty方法非常慢,但不知何故我怀疑,在这种情况下:链只能回到 1 级,到Object.prototype.

我发布的第二个链接包含一些统计数据和速度比较,但众所周知,它们在现实世界中几乎没有任何意义。谁能指出我关于 JS 和基准测试的好文章的方向(除了John Resig博客上的那篇?

所以,只是出于好奇:你们中有人发现这种方法有问题吗?更多信息: Object.prototype: 不变,undefinedis undefined,没有全局变量没有框架,并且这个方法不是盲目实现的(if (!Array.prototype.unique){...}

4

3 回答 3

4

这是一个正确考虑类型的实现,比简单的嵌套循环快得多,并保持原始数组的顺序:

Array.prototype.unique = function(){
    var r, o, i, j, t, tt;
    r = [];
    o = {};
    for(i = 0; i < this.length; i++){
       t = this[i];
       tt = o[t] = o[t] || [];
       for(j = 0; j < tt.length; j++)
           if(tt[j] === this[i])
               break;
       if(j == tt.length)
           r.push(tt[j] = t);
     }
     return r;
}

我做了一个JSPerf 来比较这些实现

  • unique1 是嵌套循环。
  • unique2 是您链接到的快速算法。
  • unique3 是您的版本。
  • unique4 是我的。
    添加
  • unique5 是 Kooilnc 的答案
  • unique6 是 primvdb 的答案

虽然 unique2 是最快的,但它有它认为的问题"1"并且1是平等的。unique4 在速度方面排在第三位,但比 unique1 快得多,并且输出正确。所有四种变体实际上都给出了不同的输出:

=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique1()
// ["1", 4, 1, 2, 3, "2", "3", "4", "true", true]

=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique2()
// [1, "2", "3", "4", true]

=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique3()
// [1, 2, 3, 4, "true"]

=> [1, "1", 1, 2, 3, 4, 1, 2, 3, "2", "3", "4", "true", "true", true].unique4()
// [1, "1", 2, 3, 4, "2", "3", "4", "true", true]
于 2012-08-20T19:10:38.173 回答
3

尽管没有得到广泛支持,但您可以使用Set(来自 ECMAScript Harmony)。它是原生的,所以它不应该对性能产生太大影响(例如,不需要像那样寻找实际的索引indexOf)。主要优点是您可以无缝地使用它来跟踪您已经拥有的项目,包括对象,并且您可以考虑相同的对象:

Array.prototype.unique = function() {
    'use strict';
    var ret = [];
    var used = new Set();
    for (var i = 0; i < this.length; i++) {
        if (!used.has(this[i])) {
            ret.push(this[i]);
            used.add(this[i]);
        }
    }
    return ret;
};

var a = {};
var b = {};
[1, 2, 1, a, a, null, b].unique(); // [1, 2, a, null, b]
于 2012-08-20T18:38:57.517 回答
0

看起来不错,但正如Felix Kling指出的那样,它仅适用于包含原语的数组。这个有点复杂,但适用于我认为的所有类型:

Array.prototype.unique = function(){
  'use strict';
  var im = {}, uniq = [];
  for (var i=0;i<this.length;i++){
    var type = (this[i]).constructor.name, 
    //          ^note: for IE use this[i].constructor!
        val = type + (!/num|str|regex|bool/i.test(type) 
               ? JSON.stringify(this[i]) 
               : this[i]);
    if (!(val in im)){uniq.push(this[i]);}
    im[val] = 1;
  }
  return uniq;
}
//testing
var test = [1,2,'string',[1,2,3],'string','1','2',
            {yada:'yada'},/[a-z]/i,2,1,1,2,[1,2,3],
            false, new Date(),/[a-b]/i,5,/[a-z]/i,'false']
   ,testunique = test.unique();
//=> testunique now
//   [1,2,string,[1,2,3],1,2,[object Object],
//    /[a-z]/i,false,Mon Aug 20 2012 20:20:11 GMT+0200,
//    /[a-b]/i,5,false]
于 2012-08-20T18:22:25.407 回答