# 7、数组结构方法和去重(带上map和forEach共15个)

TIP

数组也是对象数据类型的 typeof [] // object 数组也有属性名,只不过属性名是数字,我们把数字属性名叫做索引 类数组:使用从零开始,且自然递增的整数做键名,并且定义了length表示元素个数的对象,我们就认为它是类数组对象;

数组中常用的方法

  • 1.方法的意义和作用
  • 2.方法需要的参数
  • 3.方法的返回值
  • 4.方法执行后原有数组是否改变

# 数组的增删

方法名 含义 参数 返回值 原有数组是否改变
unshift 将数据添加到一个数组的开头 新增加的内容 新增后数组的最新长度
push 将数据添加到一个数组的末尾 一到多个,需要新增加的内容 新增后数组的最新长度
pop 移出数组中最后一个元素 被删除的这一项内容
shift 移出数组中第一个元素 被删除的这一项内容

也可以把数组当作普通对象,使用对象键值对的操作,给其设置新的属性(索引):

  • 增加:ary[ary.length]=向数组的末尾追加了新的内容
  • 删除:
    • delete删除:delete ary[索引]删除指定索引这一项(当前项被删除后,原有数组其它项的索引不会改变;当前数组的length也不会改变;)
    • ary.length- -:删除数组最后一项

# 数组的修改

方法名 含义 参数 返回值 原有数组是否改变
splice(n,m) 从索引n开始删除m个元素 n,m 被删除的会以一个新数组返回
splice(n) 从索引n开始删除到末尾 n 被删除的会以一个新数组返回
splice(0) 清空数组 0 被清空的会以一个新数组返回
splice(n,m,x) 从索引n开始用x替换m个元素,如果m为0,则表示把x增加到n的前面 n,m,x 被替换的会以一个新数组返回

# 数组的查询

方法名 含义 参数 返回值 原有数组是否改变
slice(n,m) 从索引n开始找到m处(不包含m) 注意:某一项的话需要用项数减一来获取索引 n,m 把找到的部分以一个新数组返回 不变
slice(n) 从索引n开始找到末尾 n 把找到的部分以一个新数组返回 不变
slice()/(0) 克隆数组 0 克隆原有数组并以一个新数组返回 不变

slice支持负数索引,如果传递的索引为负数,浏览器解析的时候是按照 总长度+负数索引 来处理的,如果开始索引大于结束索引,则返回空数组

# 数组的拼接

方法名 含义 参数 返回值 原有数组是否改变
concat(数组) 用于连接两个或多个数组 一个或多个数组也可以是具体值 连接后的数组 不变
concat() 克隆数组 克隆后的数组 不变

# 数组的转化

方法名 含义 参数 返回值 原有数组是否改变
toString() 把数组转换为字符串 数组的每一项在字符串中用逗号隔开(一个字符串) 不变
join(符号) 把数组的每一项按照指定连接符拼接成字符串 连接符 指定连接符拼接成的字符串 不变

var total = eval(ary.join('+')) // total = 数组中各项的和

# 数组的排序

方法名 含义 参数 返回值 原有数组是否改变
reverse() 用于颠倒数组中元素的顺序 颠倒后的数组 变为颠倒后的数组
sort() 把数组进行排序 不填则只给10以下的数排序或填写函数 排序后的数组 变为排序后的数组
ary.sort(function(a,b){
  return a-b; // 实现数组按升序排列
  return b-a; // 实现数组按降序排列
})
// 实现原理:每次拿出数组中的当前项和后一项,每一次比较都会让匿名函数执行一次,不仅执行,而且还给匿名函数传递了两个参数a,b
// a:当前项
// b:当前项的后一项
// 在匿名函数中,如果return的结果是一个大于0的数,让a和b交换位置
// 如果return 的结果是一个小于等于0,则不交换; 
// 注意:如果sort中什么都不填的话,默认会把数组元素转换为字符串,然后按照字符的UTF-16编码顺序来排序;

# 验证数组中是否包含某一项

# A.indexOf(B)/lastIndexOf

A.indexOf(B)/lastIndexOf

和字符串相同,获取B项在数组A中第一次/最后一次出现位置的索引,如果数组中没有这一项返回-1(不兼容IE6-8,在不考虑兼容的情况下,我们可以根据这个规律验证数组中是否包含某项) 兼容写法:

Array.prototype.myIndexOf = function(value){
	var result =- 1;
	for(var i = 0;i < this.length;i++ ){
    if(value === this[i]){
			result = i;
			break;
		}
		return result;
  }
}

# 遍历数组中的每一项(ES5独有IE6-8不兼容)

# every()

every()

对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true ,只要有一项返回false,就直接返回false; 注意:如果调用every方法的数组为一个空数组,那么不会运行传递的回调函数,直接返回true(此处涉及every的判别机制问题)

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item, index, array){
  return (item > 2);
});
console.log(everyResult) // false

# some()

some()

对数组中的每一项运行给定函数,只要有一项运行函数后返回 true ,就直接返回 true ,否则返回false 注意:如果调用some方法的数组为一个空数组,那么不会运行传递的回调函数,直接返回false(此处涉及some的判别机制问题)

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.some(function(item, index, array){
  return (item > 2);
});
console.log(everyResult)//true

# filter()

filter()

对数组中的每一项运行给定函数,返回运行给定函数返回 true 的项组成的数组

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item, index, array){
  return (item > 2);//如果没有符合的项,filterResult则是一个空数组
});
console.log(filterResult); // [3,4,5,4,3]

# forEach()

forEach()

对数组中的每一项运行给定函数。这个方法没有返回值。(本质上与for循环迭代一样) forEach:不修改原来的数组,无返回值 forEach:遍历数组中的每一项,不对原来的数组进行修改。不支持return,没有返回值。第二个参数表示传的是谁就把function中的this修改为谁,可不写默认为window

ary.forEach(function(value,index){
  //=>数组中有多少项,当前回调函数执行多少次;每一次传递进来的value就是当前遍历数组这一项的值,index就是遍历这一项的索引
},this(可选))

# map()

map()

对数组中的每一项运行给定函数,返回是一个新的数组,数组中包含每次运行给定函数返回的结果。 map:不修改原来的数组,支持返回值(返回的是一个数组) map:和forEach非常相似,map 的回调函数中支持return返回值,return的是什么相当于把数组中这一项改为什么(但是并不改变原来的数组,只是相当于把原数组克隆了一份,修改了克隆的数组里面每一项的值) 第二个参数表示传的是谁就把function中的this修改为谁,可不写默认为window。

var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item, index, array){
  return item * 2;
});
console.log(mapResult); //[2,4,6,8,10,8,6,4,2]

ary.map(function(value,index){
  //=>数组中有多少项,当前回调函数执行多少次;每一次传递进来的value就是当前遍历数组这一项的值,index就是遍历这一项的索引
  return xxx; //=>RETURN后面返回的结果就是把当前遍历的这一项修改为xxx
});
// map执行完成后返回一个新的数组

以上方法都不会修改数组中的包含的值

# 归并方法:

# reduce(fn,initValue)和 reduceRight(fn,initValue)(IE6-8不兼容)

TIP

这两个方法都会循环数组的所有项,然后构建一个最终返回的值。都支持两个参数,第一个参数为循环每一次执行的回调函数,第二个参数为循环第一次的时候给的回调函数中prev的初始值;区别在于从哪头开始遍历数组,除此之外,它们完全相同; 注意:不设置初始值的时候,会把数组中的第一项的值赋给回调函数中的prev,从第二项开始循环,fn执行的次数为 数组的长度-1。如果设置了prev的初始值,就会从第一项开始循环。fn的执行次数为 数组的长度 每次循环执行回调函数都会默认传递四个参数prev, cur, index, array prev:上一次累积的值(前提是回调函数中必须有返回值,没有返回值则从第二次开始就是undefined) cur:当前循环的那一项 index:当前项的索引 array:原始数组

// 不设置初始值
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
  return prev + cur;
});
console.log(sum);//15

// 获取两个对象中age相加的值(设置初始值相当于把第一次循环时prev的值设置为了0)
var values = [{name:'ceshi',age:9},{name:'ceshi',age:21}];
var sum = values.reduce(function(prev, cur, index, array){
  第一次循环prev为0
    return prev + cur.age;
},0);
console.log(sum);

# ES6中新增的数组方法

find(fn,this(可选)):返回数组中符合条件的项,不修改原来的数组

findIndex(fn,this(可选)):返回数组中符合条件的项的索引,不修改原来的数组

*这两个方法都可以接受第二个参数,用来改变回调函数fn中的this。*它的第一个参数是一个回调函数,回调函数支持三个参数依次为当前的值、当前的索引和原数组。所有数组成员依次执行该回调函数,直到找出第一个执行回调函数后返回值为true的成员,然后返回该成员,停止循环。findIndex返回的是符合条件的数组成员的索引,如果没有符合条件的成员(回调函数都返回false),find返回undefined,findIndex返回-1。

includes:返回一个布尔值,表示某个数组是否包含给定的值,

flat:用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响

# 数组的去重

思想:

  • 1、双循环:拿数组中的每一项和它后面的每一项比较,如果相同则删除后面的那一项
for(var i=0;i<ary.length-1;i++){
	var val=ary[i];
	for(var k=i+1;k<ary.length;k++){//拿第一项从第二项开始作比较所以k从1开始
		if(val===ary[k]{
			ary.splice(k,1;
			//splice会导致数组塌陷,即删除项后面的每一项的索引都会-1,此时j++会跳过删除项后面的第一项,从第二项开始比较
			k--;//解决数组塌陷问题,相当于k没加没减继续拿
		}
//		val===ary[k]?ary.splice(k,1):k++;//这种方式也可以解决数组塌陷问题,需要把大循环中的i++删除,
	}
}
  • 2、用indexOf来验证当前数组中是否包含某一项,包含则把当前项删除掉(不兼容IE6-8);
function uniqe(ary) {
    for (var i = 0; i < ary.length;) {
        var cur = ary[i];
        var val = ary.slice(i + 1);//当前项后面的项组成的一个新数组
        val.indexOf(cur) > -1 ? ary.splice(i, 1) : i++;
    }
    return ary;
}
  • 3、利用对象中属性名不能重复的原理,循环数组中的每一项,把每一项当做属性名在对象中验证其值是否存在(typeof obj[cur[i]]==='undefined'),如果在对象中不存在(true),把最后一项的值赋值给当前项,然后删除最后一项;
function oSort(ary) {
  var obj={};
  for(var i=0;i<ary.length;i++){
      var cur=ary[i];
      if(obj[cur]===cur){
          ary[i]=ary[ary.length-1];
          ary.length--;
          i--;
          continue;
      }
      obj[cur]=cur;
  }
  obj=null;
  return ary;
}
  • 4、先将原数组排序,排完序之后重复的项都会靠在一起,创建一个空的数组,然后把原数组中的第一项放到新数组中,然后循环原数组,从第二项开始,和新数组中的最后一项比较是否相等,如果不相等,则把原数组中的这一项添加到新数组的末尾,最后返回新数组即可;(有局限性:因为在去重之前会把原数组排序,所以会改变原来的数组)
Array.prototype.myUnique=function(){
  var ary=this.sort(),
    val=[];
  val.push(ary[0]);
  fot(var i=1;i<ary.length;i++){
    var obj=ary[i];
    if(obj!==val[val.length-1]){
      val.push(obj);
    }
  }
  return val;
}

# 冒泡排序

原理:

让数组中的当前项和后面的每一项进行比较,如果当前项大于后一项,我们让两者交换位置(小—大)

function bubble(ary) {
    //->外层循环控制的是比较的轮数:
    for (var i = 0; i < ary.length - 1; i++) {
        //->里层循环控制每一轮比较的次数
        for (var k = 0; k < ary.length - 1 - i; k++) {
            //ary[k]:当前本次拿出来这一项
            //ary[k+1]:当前项的后一项
            if (ary[k] > ary[k + 1]) {
                //当前项比后一项大,我们让两者交换位置
                var temp = ary[k];
                ary[k] = ary[k + 1];
                ary[k + 1] = temp;
            }
        }
    }
    return ary;
}

每一轮从前到后两两比较,虽然不一定实现最后的排序效果,但是可以把当前最大的放在末尾 具体比较的轮数:ary.length-1 数组有多长,我们只需要把总长度-1个数分别放在末尾,即可实现最后的排序 对于数组[12, 13, 23, 14, 16, 11]; 第一轮比较5次:一共六个,不需要和自己比较 第二轮比较4次:一共六个,不用和自己比,也不用和第一轮放在末尾的那个最大值比 第三轮比较3次 每一轮比较的次数 ary.length-1(不用和自己比)-当前已经执行的轮数(执行一轮向末尾放一个最大值,这些值不需要再比较)

# 快速排序

原理

先找数组中的中间项,然后和其余项作比较,比中间项小的放在左边(新数组),大的放在右边(新数组);然后使用递归方法,把左边的数组和右边的数组再次调用快速排序,最后把递归后的结果(都是数组)连接起来并返回

function quick(ary){
//如果传进来的数组只有一项或者是空的,我们则不再继续取中间项拆分
	if(ary.length<=1)return ary;
//获取中间项的索引:把中间项的值获取到,在原有数组中删除中间项
	var oIndex=Math.floor(ary.length/2);
	var val=ary.splice(oIndex,1)[0];//->splice返回的是个数组,数组中包含了删除的那个内容
	//用中间项和数组中剩下的项作比较,比中间项大放在右边,小的放在左边(左右两边都是新数组)
	var aryLeft=[],aryRight=[];
	for(var i=0;i<ary.length;i++){
		var cur=ary[i];
		cur>val?aryRight.push(cur):aryLeft.push(cur);
	}
	//然后使用递归方法,把左边的数组和右边的数组也快速排序
	//最后把递归后的结果(都是数组)和中间项连接起来,并返回
	return quick(aryLeft).concat(val,quick(aryRight));
}

# 插入排序法

原理:

类似抓牌,先从数组中拿出一项(一般都是第一项)放在手中(一个新的空数组中),然后循环数组中剩下的项,和手中现有的项作比较,如果数组中的项比手中当前项小,就把数组中的项放在手中当前项的前面。如果数组中的项比手中当前项大,则不进行操作,进行下一次循环比较。如果数组中的项比手中最后一项还要大,则把数组中的项放在手中最末尾的位置;

function insert(ary) {
  var handAry=[];
  // 从数组中抽取第一项放到手中
  handAry.push(ary[0]);
  // 拿数组中剩余的项(i从1开始)和手中的项做比较
  for (var i = 1; i <ary.length; i++) {
    var val= ary[i];//数组中剩下的项
    for (var k = 0; k <handAry.length; k++) {
      // handAry[k]:当前比较的手里的牌
      // 新抓的牌比当前比较的这张牌小,我们就把新抓的放在当前比较这张牌的前面
      if(val<handAry[k]){
        handAry.splice(k,0,val);
        break;
      }
      // 如果数组中的项比较到了最后一项以上条件还未满足,则说明数组中的项比手中最后一项还要大,就把它添加到末尾
      if(k===len-1){
        handAry.push(val);
        break;
      }
    }
  }
  return handAry;
}

# 数组排序+去重(自己总结的wihle循环方法)

function mySortAndUnique(ary) {
  var obj={},val=[],len=ary.length;
  //去重
  while (len--){
    obj[ary[len]]=ary[len];
  }
  // 利用对象属性的不重复性
  for (var key in obj) {
    if(obj.hasOwnproperty(key)){
      val.push(obj[key]);
    }
  }
  obj = null;
  return val;
}
上次更新: 4/16/2021, 11:27:51 PM