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
}
我希望这是一个缓慢的检查器,尤其是因为没有首先进行排序。我对提高排序能力等感兴趣,所以我想我会得到较早的审查。
#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来说,会使该实现变慢。优势在于简单性
评论
与如何快速查找唯一列表项相关?使用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]