必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

主要推崇的方法是将利用数组元素当作对象key来

2019-11-08 22:32 来源:未知

也谈JavaScript数组去重

2017/01/07 · JavaScript · 数组

初藳出处: TooBug(@TooBug)   

JavaScript的数组去重是七个陈规陋习的话题了。随意搜大器晚成搜就能够找到超多不一致版本的解法。

前天在和讯上见到大器晚成篇作品,也写数组去重,首要重视的艺术是将选用数组成分充任对象key来去重。笔者在博客园转载了“用对象key去重不是个好方法…”然后小编问什么才是引入的方法。

细想一下,那样贰个近乎简单的急需,若是要造成完善,涉及的学问和内需小心的地点实在不菲,于是诞生此文。

JavaScript的数组去重是二个陈腔滥调的话题了。随意搜生机勃勃搜就能够找到比超级多不一样版本的解法。

js数组去重新二种方式

使用

1.只要不依赖于其余情势,初级写法应该是:

概念再度(相等卡塔 尔(阿拉伯语:قطر‎

要去重,首先得定义,什么叫作“重复”,即现实到代码来讲,七个数据在什么样景况下能够算是特出的。那并不是多个非常轻便的难点。

对于原始值来讲,咱们相当的轻巧想到11是卓殊的,'1''1'也是相等的。那么,1'1'是相等的么?

意气风发旦这么些标题幸而说,只要回答“是”大概“不是”就能够。那么下边那几个景况就没那么轻巧了。

细想一下,那样三个像样轻松的需求,假使要产生康健,涉及的知识和须要留意的地点实在不菲。定义再一次

率先种:也是最笨的啊。

复制代码 代码如下:

function unique(arr){
    var res=[];
    for(var i=0,len=arr.length;i<len;i++){
        var obj = arr[i];
        for(var j=0,jlen = res.length;j<jlen;j++){
            if(res[j]===obj) break;            
        }
        if(jlen===j)res.push(obj);
    }
    return res;
}
var arr=[1,1,'2','1',3,4]
arr = unique(arr);
console.log(arr);

NaN

初看NaN时,超轻便把它正是和nullundefined如出生机勃勃辙的独立数据类型。但实在,它是数字类型。

JavaScript

// number console.log(typeof NaN);

1
2
// number
console.log(typeof NaN);

依附标准,相比较运算中大器晚成旦有一个值为NaN,则相比较结实为false,所以会有上边这么些看起来略蛋疼的下结论:

JavaScript

// 全都是false 0 < NaN; 0 > NaN; 0 == NaN; 0 === NaN;

1
2
3
4
5
// 全都是false
0 < NaN;
0 > NaN;
0 == NaN;
0 === NaN;

以最终贰个表明式0 === NaN为例,在正规中有鲜明规定():

  1. If Type(x) is Number, then
    a. If x is NaN, return false.
    b. If y is NaN, return false.
    c. If x is the same Number value as y, return true.
    d. If x is +0 and y is −0, return true.
    e. If x is −0 and y is +0, return true.
    f. Return false.

那象征任何涉及到NaN的景观都不可能大约地利用相比运算来剖断是不是等于。相比科学的不二秘诀只好是运用isNaN()

JavaScript

var a = NaN; var b = NaN;   // true console.log(isNaN(a) && isNaN(b));

1
2
3
4
5
var a = NaN;
var b = NaN;
 
// true
console.log(isNaN(a) && isNaN(b));

要去重,首先得定义,什么叫作“重复”,即现实到代码来说,五个数据在怎么景况下能够算是格外的。那并非多个非常轻便的标题。

Array.prototype.unique1 = function () {
var r = new Array();
label:for(var i = 0, n = this.length; i < n; i++) {
for(var x = 0, y = r.length; x < y; x++) {
if(r[x] == this[i]) {
continue label;
}
}
r[r.length] = this[i];
}
return r;
}
其次种:那么些正则天书相符。

<script language=javascript>
var isNumeric = function(x) {
   // returns true if x is numeric and false if it is not.
   var RegExp = /^(-)?(d*)(.?)(d*)$/;
   return String(x).match(RegExp);
}
var myArray = [1,'two',3,'four',5,'six',7,'eight',9,'ten'];
var oddArray=myArray.filter(isNumeric);  // outputs: 1,3,5,7,9
var oddArray=myArray.some(isNumeric);  // outputs: true
var oddArray=myArray.every(isNumeric);  // outputs: false
var printArray =function(x, idx){
   document.writeln('['+idx+'] = '+x);
}
myArray.forEach(printArray);// outputs: [0] = 1 [1] = two [2] = 3 [3] = four [4] = 5
myArray.remove(9);
document.writeln(myArray);

 

原始值和打包对象

看完NaN是或不是头都大了。好了,我们来轻松一下,看后生可畏看原始值和包裹对象那黄金年代对朋友。

风姿浪漫旦你钻探过'a'.trim()那般的代码的话,不精晓是还是不是发生过这么的疑问:'a'大庭广众是一个原始值(字符串卡塔尔国,它干吗可以直接调用.trim()办法吧?当然,很大概您曾经清楚答案:因为JS在施行这样的代码的时候会对原始值做一次包装,让'a'化为一个字符串对象,然后实行那一个指标的点子,实施完以往再把那几个包裹对象脱掉。能够用上边包车型地铁代码来了然:

JavaScript

// 'a'.trim(); var tmp = new String('a'); tmp.trim();

1
2
3
// 'a'.trim();
var tmp = new String('a');
tmp.trim();

这段代码只是扶助我们精晓的。但包装对象这么些定义在JS中却是真实存在的。

JavaScript

var a = new String('a'); var b = 'b';

1
2
var a = new String('a');
var b = 'b';

a正是一个包装对象,它和b相近,代表四个字符串。它们都得以运用字符串的各类办法(比如trim()卡塔尔国,也得以涉足字符串运算(+号连接等卡塔尔。

但她们有二个主要的界别:类型分化!

JavaScript

typeof a; // object typeof b; // string

1
2
typeof a; // object
typeof b; // string

在做字符串比较的时候,类型的分歧会引致结果有局地竟然:

JavaScript

var a1 = 'a'; var a2 = new String('a'); var a3 = new String('a'); a1 == a2; // true a1 == a3; // true a2 == a3; // true a1 === a2; // false a1 === a3; // false a2 === a3; // false

1
2
3
4
5
6
7
8
9
var a1 = 'a';
var a2 = new String('a');
var a3 = new String('a');
a1 == a2; // true
a1 == a3; // true
a2 == a3; // true
a1 === a2; // false
a1 === a3; // false
a2 === a3; // false

如出风度翩翩辙是意味字符串a的变量,在行使严厉相比时以至不是相等的,在直觉上那是一件比较难接收的业务,在种种草销处境下,也特别轻松忽略那么些细节。

对于原始值来说,大家超轻便想到1和1是分外的,'1'和'1'也是非常的。那么,1和'1'是相等的么?

Array.prototype.unique2 = function () {
return this.sort().join(",,").replace(/(,|^)([^,]+)(,,2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(",");
}
其三种:使用对象的【hasOwnProperty】方法

复制代码 代码如下:

2.借使不考虑包容性,会用ES5里面数组的indexOf()方法。

指标和指标

在事关比较的时候,还有大概会境遇对象。具体来讲,大概能够分为三种状态:纯对象、实例对象、此外项目标指标。

纯对象

纯对象(plain object卡塔尔国具体指什么并非可怜引人注目,为收缩不须要的争辩,下文中央银行使纯对象指代由字面量生成的、成员中不含函数和日期、正则表明式等类别的目标。

若是一直拿多少个目的进行相比,不管是==还是===,千真万确都以不等于的。但是在骨子里运用时,那样的规规矩矩是或不是必然满意大家的急需?举个例证,大家的施用中有八个布局项:

JavaScript

// 原本有两特性情 // var prop1 = 1; // var prop2 = 2; // 重构代码时八个天性被内置同二个指标中 var config = { prop1: 1, prop2: 2 };

1
2
3
4
5
6
7
8
// 原来有两个属性
// var prop1 = 1;
// var prop2 = 2;
// 重构代码时两个属性被放到同一个对象中
var config = {
    prop1: 1,
    prop2: 2
};

若是在某个场景下,我们供给相比三遍运转的布置项是还是不是意气风发律。在重构前,大家分别相比较五遍运行的prop1prop2就能够。而在重构后,大家恐怕要求比较config对象所表示的布署项是否近似。在这里样的情景下,直接用==或者===来比较对象,获得的并非大家盼望的结果。

在这里么的场所下,大家恐怕需求自定义一些措施来拍卖对象的可比。屡见不鲜的或是是透过JSON.stringify()对指标开展体系化之后再相比字符串,当然这些进度不要完全可相信,只是三个思路。

假定您以为那个场馆是胡编的话,能够再回忆一下断言库,相通是依附对象成员,剖断结果是还是不是和预期切合。

实例对象

实例对象主要指通过构造函数(类卡塔 尔(阿拉伯语:قطر‎生成的指标。那样的靶子和纯对象近似,直接相比都以莫衷一是的,但也会遇上供给看清是不是是同风姿罗曼蒂克对象的动静。平常来讲,因为这种对象有相比复杂的内部结构(甚至有局地数额在原型上卡塔 尔(英语:State of Qatar),不可能直接从表面相比是或不是等于。相比较可靠的判定方法是由构造函数(类卡塔 尔(英语:State of Qatar)来提供静态方法恐怕实例方法来判别是还是不是等于。

JavaScript

var a = Klass(); var b = Klass(); Klass.isEqual(a, b);

1
2
3
var a = Klass();
var b = Klass();
Klass.isEqual(a, b);

其余对象

任何对象首要指数组、日期、正则表明式等那类在Object根基上派生出来的指标。那类对象各自有各自的特殊性,日常必要依照气象来布局剖断方法,决定八个目的是不是等于。

比方,日期对象,也许须求经过Date.prototype.getTime()办法得到时间戳来判别是还是不是意味相似时刻。正则表明式只怕必要经过toString()措施赢得到原始字面量来推断是还是不是是相符的正则表达式。

假定那么些难点万幸说,只要回答“是”恐怕“不是”就可以。那么下边这一个处境就没那么轻便了。NaN

Array.prototype.unique3 = function() {
var temp = {}, len = this.length;
for(var i=0; i < len; i++) {
var tmp = this[i];
if(!temp.hasOwnProperty(tmp)) {
temp[this[i]] = "my god";
}
}

if (!Array.prototype.every)
{
  Array.prototype.every = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

function unique(arr){
    var res =[];
    for(var i=0,len=arr.length;i<len;i++){
        var obj = arr[i];
        if(res.indexOf(obj)===-1) res.push(obj);
    }
    return res;
}
var arr=[1,1,'2','1',3,4]
arr = unique(arr);
console.log(arr);// arr=[1,'2','1',3,4]

==和===

在局地小说中,看到某部分数组去重的格局,在认清成分是否等于时,使用的是==正如运算符。远近著名,那些运算符在可比前会先查看成分类型,当类型不菲年老成致时会做隐式类型转换。那实际是生机勃勃种卓殊不严格的做法。因为不可能区分在做回避类型调换后值同样的要素,举例0''falsenullundefined等。

还要,还大概有希望现身部分只可以黄人问号的结果,比如:

JavaScript

[] == ![]; //true

1
[] == ![]; //true

初看NaN时,非常轻便把它当成和null、undefined同样的独门数据类型。但实在,它是数字类型。// number

len = 0;
var tempArr=[];
for(var i in temp) {
tempArr[len++] = i;
}
return tempArr;
}
第二种:先排序,前项比后项。这几个办法挺轻巧的,但也实用

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          !fun.call(thisp, this[i], i, this))
        return false;
    }

3.利用ES5数组里的filter过滤:

Array.prototype.indexOf()

在大器晚成部分版本的去重中,用到了Array.prototype.indexOf()方法:

JavaScript

function unique(arr) { return arr.filter(function(item, index){ // indexOf重回第一个索引值, // 假使当前索引不是第三个目录,表达是双重值 return arr.indexOf(item) === index; }); }

1
2
3
4
5
6
7
function unique(arr) {
    return arr.filter(function(item, index){
        // indexOf返回第一个索引值,
        // 如果当前索引不是第一个索引,说明是重复值
        return arr.indexOf(item) === index;
    });
}

JavaScript

function unique(arr) { var ret = []; arr.forEach(function(item){ if(ret.indexOf(item) === -1){ ret.push(item); } }); return ret; }

1
2
3
4
5
6
7
8
9
function unique(arr) {
    var ret = [];
    arr.forEach(function(item){
        if(ret.indexOf(item) === -1){
            ret.push(item);
        }
    });
    return ret;
}

既然=====在要素相等的可比中是有远大差异的,那么indexOf的情景又如何呢?超越33.33%的篇章都尚未谈到那点,于是只可以求助规范。通过正规(卡塔 尔(阿拉伯语:قطر‎,我们知晓了indexOf()运用的是严俊比较,约等于===

再度重申:根据前文所述,===不能够管理NaN的相等性剖断。

console.log(typeof NaN);

Array.prototype.unique4 = function () {
var temp = new Array();
this.sort();
for(i = 0; i < this.length; i++) {
if( this[i] == this[i+1]) {
continue;
}
temp[temp.length]=this[i];
}
return temp;

    return true;
  };
}
if (!Array.prototype.filter)
{
  Array.prototype.filter = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

function unique(a) {

  var res = a.filter(function(item, index, array) {
    return array.indexOf(item) === index;
  });

  return res;
}


var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); 

Array.prototype.includes()

Array.prototype.includes()是ES二〇一四中新扩张的方法,用于判别数组中是还是不是带有某些成分,所以地点运用indexOf()措施的首个版本能够改写成如下版本:

JavaScript

function unique(arr) { var ret = []; arr.forEach(function(item){ if(!ret.includes(item)){ ret.push(item); } }); return ret; }

1
2
3
4
5
6
7
8
9
function unique(arr) {
    var ret = [];
    arr.forEach(function(item){
        if(!ret.includes(item)){
            ret.push(item);
        }
    });
    return ret;
}

那么,你猜猜,includes()又是用怎么着情势来比较的吧?要是想当然的话,会以为明确跟indexOf()同大器晚成喽。但是,工程师的社会风气里最怕想当然。翻黄金年代翻标准,开掘它事实上是利用的另豆蔻梢头种比较艺术,叫作“SameValueZero”相比较()。

  1. If Type(x) is different from Type(y), return false.
  2. If Type(x) is Number, then
    a. If x is NaN and y is NaN, return true.
    b. If x is +0 and y is -0, return true.
    c. If x is -0 and y is +0, return true.
    d. If x is the same Number value as y, return true.
    e. Return false.
  3. Return SameValueNonNumber(x, y).

注意2.a,如果xy都是NaN,则返回true!也就是includes()是足以准确推断是还是不是含有了NaN的。大家写生机勃勃段代码验证一下:

JavaScript

var arr = [1, 2, NaN]; arr.indexOf(NaN); // -1 arr.includes(NaN); // true

1
2
3
var arr = [1, 2, NaN];
arr.indexOf(NaN); // -1
arr.includes(NaN); // true

能够见见indexOf()includes()对待NaN的表现是全然不相近的。

基于标准,比较运算中后生可畏经有三个值为NaN,则相比较结实为false,所以会有上边那几个看起来略蛋疼的结论:// 全是false

}

    var res = new Array();
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
      {
        var val = this[i]; // in case fun mutates this
        if (fun.call(thisp, val, i, this))
          res.push(val);
      }
    }

 

一些方案

从上边的一大段文字中,我们可以观望,要认清三个因素是还是不是等于(重复卡塔尔国并非生机勃勃件简单的事情。在询问了这么些背景后,我们来看有个别前方未有涉嫌到的去重方案。

0 < NaN;

上面是从前日常用的,效率也很好。有一些想hash表的以为。

    return res;
  };
}
if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

方法二

遍历

再也遍历是最轻便想到的去重方案:

JavaScript

function unique(arr) { var ret = []; var len = arr.length; var isRepeat; for(var i=0; i<len; i++) { isRepeat = false; for(var j=i+1; j<len; j++) { if(arr[i] === arr[j]){ isRepeat = true; break; } } if(!isRepeat){ ret.push(arr[i]); } } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function unique(arr) {
    var ret = [];
    var len = arr.length;
    var isRepeat;
    for(var i=0; i<len; i++) {
        isRepeat = false;
        for(var j=i+1; j<len; j++) {
            if(arr[i] === arr[j]){
                isRepeat = true;
                break;
            }
        }
        if(!isRepeat){
            ret.push(arr[i]);
        }
    }
    return ret;
}

双重遍历还只怕有五个优化版本,可是原理和复杂度差不离统统相仿:

JavaScript

function unique(arr) { var ret = []; var len = arr.length; for(var i=0; i<len; i++){ for(var j=i+1; j<len; j++){ if(arr[i] === arr[j]){ j = ++i; } } ret.push(arr[i]); } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
13
function unique(arr) {
    var ret = [];
    var len = arr.length;
    for(var i=0; i<len; i++){
        for(var j=i+1; j<len; j++){
            if(arr[i] === arr[j]){
                j = ++i;
            }
        }
        ret.push(arr[i]);
    }
    return ret;
}

这种方案没什么大主题材料,用于去重的可比部分也是团结编写达成(arr[i] === arr[j]卡塔 尔(阿拉伯语:قطر‎,所以相等性能够和煦针对上文谈起的种种处境再说特殊管理。独一比较受诟病的是使用了双重循环,时间复杂度相比高,品质通常。

0 > NaN;

Array.prototype.unique5 = function() {
var res = [], hash = {};
for(var i=0, elem; (elem = this[i]) != null; i++) {
if (!hash[elem])
{
res.push(elem);
hash[elem] = true;
}
}
return res;
}

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        fun.call(thisp, this[i], i, this);
    }
  };
}
if (!Array.prototype.map)
{
  Array.prototype.map = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

 

选用对象key来去重

JavaScript

function unique(arr) { var ret = []; var len = arr.length; var tmp = {}; for(var i=0; i<len; i++){ if(!tmp[arr[i]]){ tmp[arr[i]] = 1; ret.push(arr[i]); } } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
function unique(arr) {
    var ret = [];
    var len = arr.length;
    var tmp = {};
    for(var i=0; i<len; i++){
        if(!tmp[arr[i]]){
            tmp[arr[i]] = 1;
            ret.push(arr[i]);
        }
    }
    return ret;
}

这种形式是应用了对象(tmp卡塔 尔(英语:State of Qatar)的key不得以重新的风味来拓宽去重。但鉴于目的key只可以为字符串,由此这种去重方法有不菲局限性:

  1. 敬敏不谢区分隐式类型转变来字符串后相似的值,举个例子1'1'
  2. 无法管理复杂数据类型,比如对象(因为对象作为key会产生[object Object]
  3. 新鲜数据,例如'__proto__'会挂掉,因为tmp对象的__proto__性格无法被重写

对此第一点,有人提议可感觉对象的key扩张二个体系,可能将品种放到对象的value中来减轻:

JavaScript

function unique(arr) { var ret = []; var len = arr.length; var tmp = {}; var tmpKey; for(var i=0; i<len; i++){ tmpKey = typeof arr[i] + arr[i]; if(!tmp[tmpKey]){ tmp[tmpKey] = 1; ret.push(arr[i]); } } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function unique(arr) {
    var ret = [];
    var len = arr.length;
    var tmp = {};
    var tmpKey;
    for(var i=0; i<len; i++){
        tmpKey = typeof arr[i] + arr[i];
        if(!tmp[tmpKey]){
            tmp[tmpKey] = 1;
            ret.push(arr[i]);
        }
    }
    return ret;
}

该方案也还要缓慢解决第多个难题。

而第三个难题,假使像上文所说,在同意对目的实行自定义的相比法则,也能够将指标系列化之后作为key来使用。这里为简易起见,使用JSON.stringify()开展类别化。

JavaScript

function unique(arr) { var ret = []; var len = arr.length; var tmp = {}; var tmpKey; for(var i=0; i<len; i++){ tmpKey = typeof arr[i] + JSON.stringify(arr[i]); if(!tmp[tmpKey]){ tmp[tmpKey] = 1; ret.push(arr[i]); } } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function unique(arr) {
    var ret = [];
    var len = arr.length;
    var tmp = {};
    var tmpKey;
    for(var i=0; i<len; i++){
        tmpKey = typeof arr[i] + JSON.stringify(arr[i]);
        if(!tmp[tmpKey]){
            tmp[tmpKey] = 1;
            ret.push(arr[i]);
        }
    }
    return ret;
}

0 == NaN;

还是能有种更简单明了的写法:

    var res = new Array(len);
    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this)
        res[i] = fun.call(thisp, this[i], i, this);
    }

法一是将原数组中的成分和结果数组中的元素风流倜傥一相比,大家能够换个思路,将原数组中再度元素的末尾二个要素归入结果数组中。

Map Key

能够看见,使用对象key来拍卖数组去重的标题,其实是风流倜傥件相比较费心的事体,管理倒霉十分轻便招致结果不得法。而那个题目标根本原因正是因为key在利用时有限定。

那么,能否有风姿罗曼蒂克种key使用未有界定的指标呢?答案是——真的有!那正是ES二〇一六中的Map

Map是黄金年代种新的数据类型,能够把它想象成key类型未有限制的指标。别的,它的存取使用单独的get()set()接口。

JavaScript

var tmp = new Map(); tmp.set(1, 1); tmp.get(1); // 1 tmp.set('2', 2); tmp.get('2'); // 2 tmp.set(true, 3); tmp.get(true); // 3 tmp.set(undefined, 4); tmp.get(undefined); // 4 tmp.set(NaN, 5); tmp.get(NaN); // 5 var arr = [], obj = {}; tmp.set(arr, 6); tmp.get(arr); // 6 tmp.set(obj, 7); tmp.get(obj); // 7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var tmp = new Map();
tmp.set(1, 1);
tmp.get(1); // 1
tmp.set('2', 2);
tmp.get('2'); // 2
tmp.set(true, 3);
tmp.get(true); // 3
tmp.set(undefined, 4);
tmp.get(undefined); // 4
tmp.set(NaN, 5);
tmp.get(NaN); // 5
var arr = [], obj = {};
tmp.set(arr, 6);
tmp.get(arr); // 6
tmp.set(obj, 7);
tmp.get(obj); // 7

鉴于Map使用单独的接口来存取数据,所以不用忧郁key会和放手属性重名(如上文提到的__proto__)。使用Map改写一下我们的去重方法:

JavaScript

function unique(arr) { var ret = []; var len = arr.length; var tmp = new Map(); for(var i=0; i<len; i++){ if(!tmp.get(arr[i])){ tmp.set(arr[i], 1); ret.push(arr[i]); } } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
function unique(arr) {
    var ret = [];
    var len = arr.length;
    var tmp = new Map();
    for(var i=0; i<len; i++){
        if(!tmp.get(arr[i])){
            tmp.set(arr[i], 1);
            ret.push(arr[i]);
        }
    }
    return ret;
}

0 === NaN;

/*使用了聚众的考虑,有序不另行*/
function removeDuplicates(arr) {

    return res;
  };
}
if (!Array.prototype.some)
{
  Array.prototype.some = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

 

Set

既是都用到了ES贰零壹肆,数组那件事情无法再简单一点么?当然能够。

除了Map以外,ES二〇一五还引进了豆蔻梢头种叫作Set的数据类型。看名称就能够想到其意义,Set固然聚焦的乐趣,它差异意再度成分现身,那或多或少和数学中对集中的定义依旧相比像的。

JavaScript

var s = new Set(); s.add(1); s.add('1'); s.add(null); s.add(undefined); s.add(NaN); s.add(true); s.add([]); s.add({});

1
2
3
4
5
6
7
8
9
var s = new Set();
s.add(1);
s.add('1');
s.add(null);
s.add(undefined);
s.add(NaN);
s.add(true);
s.add([]);
s.add({});

万生龙活虎您再度增添同贰个成分的话,Set中只会存在一个。包蕴NaN也是这么。于是大家想到,这么好的特征,如果能和数组相互转换,不就足以去重了吗?

JavaScript

function unique(arr){ var set = new Set(arr); return Array.from(set); }

1
2
3
4
function unique(arr){
    var set = new Set(arr);
    return Array.from(set);
}

笔者们批评了这么久的事体,居然两行代码消除了,简直难以置信。

可是,不要在乎着喜欢了。有一句话是这么说的“不要因为走得太远而忘了怎么出发”。大家为何要为数组去重呢?因为我们想赢得不另行的成分列表。而既然已经有Set了,大家为何还要弄虚作假,使用数组呢?是还是不是在急需去重的意况下,直接动用Set就消除难点了?这些主题材料值得考虑。

以最后一个发挥式0 === NaN为例,在标准中有鲜明规定:

    var temp = {}, r = [];

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          fun.call(thisp, this[i], i, this))
        return true;
    }

function unique(a) {

  var res = [];

 

  for (var i = 0, len = a.length; i < len; i++) {

    for (var j = i + 1; j < len; j++) {

      // 这一步十二分全优

      // 若是发掘同样元素

      // 则 i 自增进入下二个生生不息相比较

      if (a[i] === a[j])

        j = ++i;

    }

 

    res.push(a[i]);

  }

 

  return res;

}

 

 

var a = [1, 1, '1', '2', 1];

var ans = unique(a);

console.log(ans); // => ["1", "2", 1]

小结

聊起底,用叁个测量试验用例计算一下文中现身的各样去重方法:

JavaScript

var arr = [1,1,'1','1',0,0,'0','0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/] console.log(unique(arr));

1
2
var arr = [1,1,'1','1',0,0,'0','0',undefined,undefined,null,null,NaN,NaN,{},{},[],[],/a/,/a/]
console.log(unique(arr));

测量试验中并未有概念对象的可比艺术,因而暗许处境下,对象不去重是精确的结果,去重是不得法的结果。

方法 结果 说明
indexOf#1 NaN被去掉
indexOf#2 NaN重复
includes 正确
双重循环#1 NaN重复
双重循环#2 NaN重复
对象#1 字符串和数字无法区分,对象、数组、正则表达式被去重
对象#2 对象、数组、正则表达式被去重
对象#3 对象、数组被去重,正则表达式被消失 JSON.stringify(/a/)结果为{},和空对象一样
Map 正确
Set 正确

最后的终极:任何脱离场景谈技巧都以妄谈,本文也长期以来。去重那道题,未有正确答案,请依照气象选拔适用的去重方法。

1 赞 3 收藏 评论

必威 1

  1. If Type is Number, then

    for (var i in arr)

    return false;
  };
}
Array.prototype.sortNum = function() {
   return this.sort( function (a,b) { return a-b; } );
}
<!--
var tmp = [5,9,12,18,56,1,10,42,'blue',30, 7,97,53,33,30,35,27,30,'35','Ball', 'bubble'];
var thirty=tmp.find(30);             // Returns 9, 14, 17
var thirtyfive=tmp.find('35');       // Returns 18
var thirtyfive=tmp.find(35);         // Returns 15
var haveBlue=tmp.find('blue');       // Returns 8
var notFound=tmp.find('not there!'); // Returns false
var regexp1=tmp.find(/^b/);          // returns 8,20    (first letter starts with b)
var regexp1=tmp.find(/^b/i);         // returns 8,19,20 (same as above but ignore case)
-->
Array.prototype.find = function(searchStr) {
  var returnArray = false;
  for (i=0; i<this.length; i++) {
    if (typeof(searchStr) == 'function') {
      if (searchStr.test(this[i])) {
        if (!returnArray) { returnArray = [] }
        returnArray.push(i);
      }
    } else {
      if (this[i]===searchStr) {
        if (!returnArray) { returnArray = [] }
        returnArray.push(i);
      }
    }
  }
  return returnArray;
}
//随机改换数组的排序
Array.prototype.shuffle = function (){  
    for(var rnd, tmp, i=this.length; i; rnd=parseInt(Math.random()*i), tmp=this[--i], this[i]=this[rnd], this[rnd]=tmp); 
 return this;
}  
<!--var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var yourArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
document.writeln(myArray.compare(yourArray)); // outputs: true;-->
Array.prototype.compare = function(testArr) {
    if (this.length != testArr.length) return false;
    for (var i = 0; i < testArr.length; i++) {
        if (this[i].compare) {
            if (!this[i].compare(testArr[i])) return false;
        }
        if (this[i] !== testArr[i]) return false;
    }
    return true;
}
//去掉数组中再一次的值var a = new Array("5","7","7"); a.unique();
Array.prototype.unique = function() {
 var data = this || [];
    var a = {}; //声澳优(Ausnutria Hyproca卡塔 尔(阿拉伯语:قطر‎个目的,javascript的指标可以当哈希表用
    for (var i = 0; i < data.length; i++) {
        a[data[i]] = true;  //设置标志,把数组的值当下标,那样就可以去掉重复的值
    }
    data.length = 0;

 

a. If x is NaN, return false.

        temp[arr[i]] = true;

    for (var i in a) { //遍历对象,把已标识的卷土而来成数组
        this[data.length] = i;
    }
    return data;
}

就算复杂度仍旧 O(n^2),不过能够看来结果区别,1 面世在了数组最前边,因为结果数组取的是因素最后二遍面世的地点。

b. If y is NaN, return false.

    for (var k in temp)

Array.prototype.addAll = function($array)
{
 if($array == null || $array.length == 0)
  return;

 

c. If x is the same Number value as y, return true.

        r.push(k);

 for(var $i=0; $i<$array.length; $i++)
  this.push($array[$i]);
}

方法三(sort)

d. If x is +0 and y is −0, return true.

    return r;

Array.prototype.contains = function($value)
{
 for(var $i=0; $i<this.length; $i++)
 {
  var $element = this[$i];
  if($element == $value)
   return true;
 }

 

e. If x is −0 and y is +0, return true.

}

 return false;
}

假使笔试面试时只答出了地方这样 O(n^2) 的方案,大概还无法使面试官满足,上面就来讲三种进级方案。

f. Return false.

看起来非常好轻易的吧

Array.prototype.indexOf = function($value)
{
 for(var $i=0; $i<this.length; $i++)
 {
  if(this[$i] == $value)
   return $i;
 }

 

那意味任何关系到NaN的景色都无法大约地接收比较运算来决断是或不是等于。比较不错的措施只可以是应用isNaN():var a = NaN;

通过和煦注明过了,代码如下:

 return -1;
}
if (!Array.prototype.lastIndexOf)
{
  Array.prototype.lastIndexOf = function(elt /*, from*/)
  {
    var len = this.length;

将数组用 sort 排序后,理论上同黄金时代的因素会被放在周围的地点,那么比较前后地方的成分就可以了。

var b = NaN;

 1 function removeDuplicates(arr) {
 2 
 3     var temp = {}, r = [];
 4 
 5     for (var i in arr)
 6 
 7         temp[arr[i]] = true;
 8 
 9     for (var k in temp)
10 
11         r.push(k);
12 
13     return r;
14 
15 }
16 
17 //用法
18 
19 var fruits = ['apple', 'orange', 'peach', 'apple', 'strawberry', 'orange', 'strawberry', 'orange'];
20 
21 var uniquefruits = removeDuplicates(fruits);
22 alert(uniquefruits);

    var from = Number(arguments[1]);
    if (isNaN(from))
    {
      from = len - 1;
    }
    else
    {
      from = (from < 0)
           ? Math.ceil(from)
           : Math.floor(from);
      if (from < 0)
        from += len;
      else if (from >= len)
        from = len - 1;
    }

 

// true

字符串去重方法:

    for (; from > -1; from--)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}
Array.prototype.insertAt = function($value, $index)
{
 if($index < 0)
  this.unshift($value);
 else if($index >= this.length)
  this.push($value);
 else
  this.splice($index, 0, $value);
}
/**
* 依据数组的下标来删除成分
*/ 
Array.prototype.removeByIndex=function($n) {  
    if($n<0){ //若是n<0,则不开展任何操作。 
      return this; 
    }else{ 
        return this.slice(0,$n).concat(this.slice($n+1,this.length)); 
    } 
}
//依赖indexOf
Array.prototype.remove = function($value)
{
 var $index = this.indexOf($value);

function unique(a) {

  return a.concat().sort().filter(function(item, pos, ary) {

    return !pos || item != ary[pos - 1];

  });

}

 

 

var a = [1, 1, 3, 2, 1, 2, 4];

var ans = unique(a);

console.log(ans); // => [1, 2, 3, 4]

console.log && isNaN;

应用正则

 if($index != -1)
  this.splice($index, 1);
}

 

原始值和包装对象

/**
* 字符串去重
* rn字符串分隔符
* $1分割后的字符串,$2字符串的目录
* 以分隔符将字符串分割,依据分割后的数组成分的个数举办巡回比较
*/
    function strUnique(){
        var str = "abc, abcd, abc, abcde, abcd, abcde";
        var ret = [];
        str.replace(/[^,]+/g, function($1, $2){
            (str.indexOf($1) == $2) && ret.push($1);
        });
        alert(ret);
        return ret;
    }

Array.prototype.removeAll = function()
{
 while(this.length > 0)
  this.pop();
}

只是难题又来了,1 和 "1" 会被排在一同,不一样的 Object 会被排在一齐,因为它们 toString() 的结果风华正茂致,所以会现身这么的失实:

看完NaN是否头都大了。好了,我们来轻便一下,看风度翩翩看原始值和包装对象那生龙活虎对相爱的人。

 1     function strUnique(){
 2         var str = "abc, abcd, abc, abcde, abcd, abcde";
 3         var ret = [];
 4         str.replace(/[^,]+/g, function($1, $2){
 5             (str.indexOf($1) == $2) && ret.push($1);
 6         });
 7         alert(ret);
 8         return ret;
 9     }
10     
11 strUnique();

Array.prototype.replace = function($oldValue, $newValue)
{
 for(var $i=0; $i<this.length; $i++)
 {
  if(this[$i] == $oldValue)
  {
   this[$i] = $newValue;
   return;
  }
 }
}

 

若是你商量过'a'.trim()那样的代码的话,不清楚是还是不是发生过这么的难点:'a'明明是一个原始值,它干吗能够直接调用.trim()方法吗?当然,很大概您早已知道答案:因为JS在实施那样的代码的时候会对原始值做叁次包装,让'a'形成二个字符串对象,然后实践那一个指标的不二秘诀,推行完事后再把那一个包裹对象脱掉。能够用下边包车型地铁代码来掌握:// 'a'.trim();

 

Array.prototype.swap = function($a, $b)
{
 if($a == $b)
  return;

var a = [1, 1, 3, 2, 1, 2, 4, '1'];

var ans = unique(a);

console.log(ans); // => [1, 2, 3, 4]

var tmp = new String;

 var $tmp = this[$a];
 this[$a] = this[$b];
 this[$b] = $tmp;
}
Array.prototype.max = function() { 
 return Math.max.apply({}, this); 

Array.prototype.min = function() { 
 return Math.min.apply({}, this); 
}
Array.prototype.splice = function(start, delLen, item){
 var len =this.length;
 start = start<0?0:start>len?len:start?start:0;
 delLen=delLen<0?0:delLen>len?len:delLen?delLen:len; 

 

TAG标签:
版权声明:本文由必威发布于必威-前端,转载请注明出处:主要推崇的方法是将利用数组元素当作对象key来