数组方法汇总

456 阅读17分钟

更新 求数组的交集:filter+include

检测是否为数组

  • [].slice (能力判断 )
  • [] instanceof Array(类型判断)
  • [].proto === Array.prototype
  • Array.isArray([]) 存在兼容问题
/**
 * 判断一个对象是否是数组,参数不是对象或者不是数组,返回false
 * @param {Object} arg 需要测试是否为数组的对象
 * @return {Boolean} 传入参数是数组返回true,否则返回false
 */
function isArray(arg) {
    if (typeof arg === 'object') {
        return Object.prototype.toString.call(arg) === '[object Array]';
    }
    return false;
 }
    var list = [1,2,3];
    Array.isArray(list);    //true

创建数组

Array.of(para):返回参数值组成的数组。

弥补构造函数Array()的不足:不同参数不同意思

字面量

Array()

     // 字面量方式:
    // 这个方法也是我们最常用的,在初始化数组的时候 相当方便
    var a = [3, 11, 8];  // [3,11,8];
    // 构造器:
    // 实际上 new Array === Array,加不加new 一点影响都没有。
    var a = Array(); // [] 
    var a = Array(3); // [,,] 
    var a = Array(3,11,8); // [ 3,11,8 ]
    let a = Array.of(3); // [3]

转为数组

扩展运算符(...):将数组转为用逗号分隔的参数序列

应用:

  • 函数调用,替代了apply方法将数组转为函数参数,解决了方法的参数不可以是数组的情况 eg:Math.max(...arr),push(...arr)
  • 合并数组,与解构赋值结合,set map generator

Array.form(val):将两类对象转成数组。

  1. 可遍历iterable对象, 字符串、Set、NodeList对象。

  2. 具有length属性的对象

    参数:

    • 参1(必需):要转化为真正数组的对象
    • 参2(可选):类似数组的map方法,对每个元素进行处理,将处理后的值放入返回的数组。
    • 参3(可选): 用来绑定this。

与扩展运算符异同:扩展本质是调用遍历器接口

转换为字符串

join(","):返回以逗号分开的字符串

默认逗号分隔符,把数组中的元素通过指定的分隔符进行分隔放入一个字符串,返回生成的字符串。

toString()

功能类似join,注意当数组和字符串操作时,js会调用这个方法将数组转为字符串

valueOf()

功能类似join

toLocaleString()

该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成

  • 如果某一项是null 转换后默认显示空字符串
  • 对于多重数组来说 toString() valueOf()返回的不一致
  • join()/toString()方法在数组元素是数组的时候,会将里面的数组也调用join()/toString(),如果是对象的话,对象会被转为[object Object]字符串
      let arr = [
        [0, 1],
        [2, 3],
        [4, [5, 6, 7]]
      ]
      console.log(arr.valueOf()) //array (3) [Array(2), Array(2), Array(2)]
      console.log(arr.toString()) // 0,1,2,3,4,5,6,7

      let b = [{ name: 'OBKoro1', age: '23' }, 'test']
      str2 = b.join() // [object Object],test

      let a = [{ name: 'OBKoro1' }, 23, 'abcd', new Date()]
      let str = a.toLocaleString() // [object Object],23,abcd,2018/5/28 下午1:52:20
 

增删改查(改变原数组)

增:splice,方括号

  • unshift:开头添加一个或多个元素,并返回新的长度。
  • push:末尾添加一个或多个元素,并返回新的长度。

修:方括号,splice,fill ,copyWithin

  • fill() 填充数组:参数:要填充数组到值,填充起始位置,结束位置。(0,this.length) 复制

  • copyWithin 指定位置的成员复制到其他位置

      copyWithin(target, start = 0, end = this.length) 
      第一个参数是开始被替换的元素位置
      要替换数据的位置范围:从第二个参数是开始读取的元素,在第三个参数前面一个元素停止读取
      数组的长度不会改变
      读了几个元素就从开始被替换的地方替换几个元素
    
        // -2相当于3号位,-1相当于4号位
        [1, 2, 3, 4, 5].copyWithin(0, -2, -1)
        // [4, 2, 3, 4, 5]
        var a=['OB1','Koro1','OB2','Koro2','OB3','Koro3','OB4','Koro4','OB5','Koro5']
        // 2位置开始被替换,3位置开始读取要替换的 5位置前面停止替换
        a.copyWithin(2,3,5)
        // ["OB1","Koro1","Koro2","OB3","OB3","Koro3","OB4","Koro4","OB5","Koro5"] 

排序

reverse():改变原数组

反转数组,返回排序后的数组

sort():改变原数组

默认升序,返回排序后的数组。

  • 本质是调用每个数组项的toString()方法,按照字符编码的顺序排序。即使都是数值,比较的也是字符串 eg:[5,10].sort()=>[10,5]

    • 接收一个比较函数:fun(val1,val2)

      val1应位于val2前则返回负数,后则返回正数,相等返回0

      return val2-val1(降) return val1-val2(升)

    //多层比较:可再优化 
     array.sort(function(a,b){
         if(a.id === b.id){// 如果id的值相等,按照age的值降序
             return b.age - a.age
         }else{ // 如果id的值不相等,按照id的值升序
             return a.id - b.id
         }
     })
冒泡排序(两层循环,两两互换)
//冒泡排序
function bubbleSort ( data ) {
    var temp = 0;
    for ( var i = data.length ; i > 0 ; i -- ){
        for( var j = 0 ; j < i - 1 ; j++){
           if( data[j] > data[j + 1] ){
               temp = data[j];
               data[j] = data [j+1];
               data[j+1] = temp;
           }
        }
    }
    return data;
}

juejin.cn/post/684490… 解析

选择排序

思想:不断比较,合适时机交换,只移动一次。每排完一轮都把最小的元素移到了最左面,再对右边的值进行排序。

实现思路:第一层循环i表示每轮指针指向的位置,将最小值min初始化为第i个元素,第二层循环从j=i+1开始,分别与min比较,如果小于min,则更新min的值,内层循环结束后;交换min元素和第i个元素的位置。以此类推进行下一轮循环,直到i=length时停止循环。当i=length时,说明小的元素已经全部移到了左面,因此无需进行内层循环了。

总结: N个元素需要排序N-1轮;

  第i轮需要比较N-i次;

  N个元素排序,需要比较n(n-1)/2次;

  选择排序的算法复杂度仍为O(n*n);

  相比于冒泡排序,选择排序的交换次数大大减少,因此速度要快于冒泡排序

function selectionSort( data ) {
    for( var i = 0; i< data.length ; i++){
        var min = data[i];
        var temp;
        var index = i;
        for( var j = i + 1; j< data.length; j++){
            if( data[j] < min ){
                min = data[j];
                index = j;
            }
        }


        temp = data[i];
        data[i] = min;
        data[index]= temp;
    }
    return data;
}
插入排序

思路:

  1、将指针指向某个元素,假设该元素左侧的元素全部有序,将该元素抽取出来,然后按照从右往左的顺序分别与其左边的元素比较,遇到比其大的元素便将元素右移,直到找到比该元素小的元素或者找到最左面发现其左侧的元素都比它大,停止;

2、此时会出现一个空位,将该元素放入到空位中,此时该元素左侧的元素都比它小,右侧的元素都比它大;

  3、指针向后移动一位,重复上述过程。每操作一轮,左侧有序元素都增加一个,右侧无序元素都减少一个。   

编码分析:

  需要两层循环,第一层循环index表示上述例子中的指针,即遍历从坐标为1开始的每一个元素;第二层循环从leftindex=index-1开始,leftindex--向左遍历,将每一个元素与i处的元素比较,直到j处的元素小于i出的元素或者leftindex<0;遍历从i到j的每一个元素使其右移,最后将index处的元素放到leftindex处的空位处。   

//插入排序
function insertionSort( data ) {
    var len = data.length;
    for (var i = 1; i < len; i++) {
        var key = data[i];
        var j = i - 1;
        while ( j >= 0 && data[j] > key) {
            data[j + 1] = data[j];
            j--;
        }
        data[j + 1] = key;
    }
    return data;
}

www.cnblogs.com/bjh1117/p/8… 解析

splice 替换/删除/添加 (改变数组)

  • (index,len,[item]):开始的索引(可以是负数-x,倒数最后x个元素),删除的数量(0就不删),添加的值(可以是一个或者多个)

    • 数组如果元素不够,会删除到最后一个元素为止。
    • 操作的元素,包括开始下标的那个元素,
    • 添加是在索引对应的元素前面添加的
       let a = [1, 2, 3, 4, 5, 6, 7];
        let item = a.splice(0,3,'添加'); // [1,2,3]
        console.log(a); // ['添加',4,5,6,7]
        
        // 从数组下标0开始,删除3个元素,并添加元素'添加'
         let b = [1, 2, 3, 4, 5, 6, 7];
        let item = b.splice(-2,3,'添加1','添加2'); // [6,7]
        console.log(b); // [1,2,3,4,5,'添加1','添加2']
        
        // 从数组最后第二个元素开始,删除3个元素,并添加两个元素'添加1''添加2'
        let item = b.splice(-1,0,'添加1','添加2'); // [] 没有删除元素,返回空数组
        console.log(b); // [1,2,3,4,5,6,'添加1','添加2',7] 在最后一个元素的前面添加两个元素

pop

删除一个数组中的最后的一个元素,并且返回这个元素

shift

删除数组的第一个元素,并且返回这个元素

delete

删除掉数组中的元素后,会把该下标出的值置为undefined,数组的长度不会变

增删改查(不改变原数组)

读:方括号

concat

concat方法用于多个数组的合并。它将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不变。参数也可以是其他类型的值。

       console.log([].concat([1],[2],[3],4)); // [1, 2, 3,4]
       //一个空数组与一个二维数组合并,二维数组的成员为[1],[2],[3],因此返回了[[1], [2], [3]],返回的是二维数组
       console.log([].concat([[1],[2],[3]])); // [[1], [2], [3]]
       console.log([].concat(4,[[5,6],[7]])); // [4, [5, 6], [7]]
       console.log([].concat.apply([],[[1],[2],[3]])); //[1, 2, 3]
       
        eg:在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组
       //利用 slice+concat
       arr.slice(0,index).concat(item,arr.slice(index))

slice

浅拷贝数组,返回子项构成的新数组. 可接收一个(左闭到头)或多个(左闭右开)参数。负参+数长

若数组是复杂数组时,拷贝后的前后两个元素还是会互相影响

    let a= [{name:'OBKoro1'}];
    let b=a.slice();
    console.log(b,a); // [{"name":"OBKoro1"}]  [{"name":"OBKoro1"}]
    // a[0].name='改变原数组';
    // console.log(b,a); // [{"name":"改变原数组"}] [{"name":"改变原数组"}]
    // b[0].name='改变拷贝数组',b[0].koro='改变拷贝数组';
    //  [{"name":"改变拷贝数组","koro":"改变拷贝数组"}] [{"name":"改变拷贝数组","koro":"改变拷贝数组"}]

元素查找

indexOf:返回给定元素在数组中的第一个索引,如果不存在则返回-1。

array.indexOf(searchElement,fromIndex)

  • fromIndex:开始查找的位置(不能大于等于数组长度,不然返回-1),接受负值,默认值为0。

lastIndexOf:方法返回指定元素,在数组中的最后一个的索引,如果不存在则返回 -1。

  • fromIndex(可选): 开始查找的位置

关于fromIndex有三个规则:

  • 正值。如果该值大于或等于数组的长度,则整个数组会被查找。
  • 负值。将其视为从数组末尾向前的偏移。(比如-2,从数组最后第二个元素开始往前查找)
  • 负值。其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。
  • 数组的indexOf搜索跟字符串的indexOf不一样,数组的indexOf使用严格相等===搜索元素,即数组元素要完全匹配才能搜索成功。
 let a=['OB',4,'Koro1',1,2,'Koro1',3,4,5,'Koro1']; // 数组长度为10
 // let b=a.lastIndexOf('Koro1',4); // 从下标4开始往前找 返回下标2
 // let b=a.lastIndexOf('Koro1',100); //  大于或数组的长度 查找整个数组 返回9
 // let b=a.lastIndexOf('Koro1',-11); // -1 数组不会被查找
 let b=a.lastIndexOf('Koro1',-9); // 从第二个元素4往前查找,没有找到 返回-1

includes() 查找数组是否包含某个元素 返回布尔

- array.includes(searchElement,fromIndex=0)
  • fromIndex(可选):默认值为0,参数表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false。负值绝对值超过长数组度,重置从0开始搜索。
  • includes方法是为了弥补indexOf方法的缺陷而出现的:indexOf方法不能识别NaN,检查是否包含某个值时要判断是否等于-1

关于遍历数组的方法

规则:
  对于空数组是不会执行回调函数的
  对于已在迭代过程中删除的元素,或者空元素会跳过回调函数
  遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
  如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。

参数: thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

        let a = [1, 2, ,3]; // 最后第二个元素是空的,不会遍历(undefined、null会遍历)
        let obj = { name: 'OBKoro1' };
        let result = a.forEach(function (value, index, array) { 
          a[3] = '改变元素';
          a.push('添加到尾端,不会被遍历')
          console.log(value, 'forEach传递的第一个参数'); // 分别打印 1 ,2 ,改变元素
          console.log(this.name); // OBKoro1 打印三次 this绑定在obj对象上
          // break; // break会报错
          return value; // return只能结束本次回调 会执行下次回调
          console.log('不会执行,因为return 会执行下一次循环回调')
        }, obj);
        console.log(result); // 即使return了一个值,也还是返回undefined
        // 回调函数也接受接头函数写法
    

find(callback[,thisArg])

定义:用于找出第一个符合条件的数组成员,并返回该成员,如果没有符合条件的成员,则返回undefined。

findIndex(callback[,thisArg])

定义:返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。 arr.findInde(function(currentValue, index, arr))

> 这两个方法都可以识别NaN,弥补了indexOf的不足.

some: 相当于||

检测数组中的是否有满足判断条件的元素,有一项返回true,则整体为true. array.some(function(currentValue, index, arr), thisValue)

every: 相当于&&

检测数组所有元素是否都符合判断条件,有一项返回false,则整体为false 。 array.every(function(currentValue, index, arr), thisValue)

forEach:

  • 无法break
  • 用return退出本次回调,进行下一次回调。它总是返回 undefined值,即使你return了一个值
  • 中断方法:
  1. 使用try监视代码块,在需要中断的地方抛出异常catch中throw new Error。

  2. 官方推荐方法(替换方法):用every和some替代forEach函数。every在碰到return false的时候,中止循环。some在碰到return true的时候,中止循环

filter:过滤原始数组,返回符合条件的新数组。

arr.filter(function(currentValue, index, arr), thisValue)

map:对数组中的每个元素都进行处理,返回新的数组

for-in:

不建议对数组采用for-in,其设计的目的是用于遍历包含键值对的对象。

原因:

  • 下标会变成字符串,而不是数值类型。使用其参与数值运算会有问题。
  • 不只数组本身的元素会被遍历到,为数组添加的一些属性,数组原型链上到属性也会被遍历。
  • 遍历到顺序是随机的。

keys()&values()&entries()

遍历键名、遍历键值、遍历键名+键值

三个方法都返回一个新的 Array Iterator 对象,对象根据方法不同包含不同的值.

for..of

在for..of中如果遍历中途要退出,可以使用break退出循环。
如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历:

reduce: 为数组提供累加器,合并为一个值

语法: array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
- callback接收的参数:初始值(或者**上一次回调函数的返回值**),**当前元素值,当前索引,调用 reduce 的数组**。
- initialValue 第一次回调的第一个参数
- 如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始.两种方式循环次数不一样。
- 如果传空数组会报错,但如果有初始值就不会。

reduceRight 从右至左累加


数组 arr 中所有元素的总和
function sum(arr) {
    return eval(arr.join("+"));
};
var sum2 = function(arr) {
    return arr.reduce(function(prev, curr, index, arr) {
     console.log(prev, cur, index);
        return prev + curr;
    });
};
log:
1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10
--------------------------------------------------------
var sum = arr.reduce((x,y)=>x+y)//求和 
var mul = arr.reduce((x,y)=>x*y)//求乘积
--------------------------------------------------------
统计数组 arr 中值等于 item 的元素出现的次数
   //reduce()-->从数组的第一项开始,逐个遍历到最后;
        function count(arr, item) {
            var count = arr.reduce(function(prev, curr) {
                return curr === item ? prev+1 : prev;
            }, 0);
            return count;
        }
        
使用 Array.reduce 替代 Array.filter 与 Array.map 的组合:遍历了两次数组
  characters.reduce((acc, character) => {
      return character.env === 'marvel'
        ? acc.concat(Object.assign({}, character, { alsoSeenIn: ['Avengers'] }))
        : acc;
    }, [])
)

应用场景

使用 Array.includes 替代 Array.indexOf

仅需要知道数组中是否包含给定元素:使用 Array.includes 替代 Array.indexOf,对象数组 Array.some

使用 Array.find 替代 Array.filter

知道经回调函数过滤后,只会剩余唯一的一项:使用 Array.find 替代 Array.filter,需要下标用findIndex

性能:为了获取所有符合回调函数过滤条件的项,Array.filter 必须遍历整个数组,而 find 当找到符合回调函数过滤条件的第一个元素时,它会立即停止往下的搜寻。不再遍历整个数组.

多条件筛选

   //根据名字和年龄多元素筛选
    function multiFilter(array, filters) {
      const filterKeys = Object.keys(filters)
      // filters all elements passing the criteria
      return array.filter(item => {
        // dynamically validate all filter criteria
        return filterKeys.every(key => {
          //ignore when the filter is empty Anne
          if (!filters[key].length) return true
          return !!~filters[key].indexOf(item[key])
        })
      })
    }

移除数组 arr 中的所有值与 item 相等的元素,要求直接在给定的 arr 数组上进行操作:从数组尾部开始动手

    function removeWithoutCopy(arr, item) {
        for (i = arr.length - 1; i >= 0; i--) {
          if (arr[i] == item) {
            arr.splice(i, 1)
          }
        }
        return arr
      }
倒着检测;不用考虑;位置影响

数组去重

普通数组:

  1. filter+includes+变量存储
  2. Array.form(new Set(arr))或[…new Set(arr)] es6 Set数据结构类似于数组,成员值是唯一的,有重复的值会自动去重。 Set内部使用===来判断是否相等,类似'1'和1会两个都保存,NaN和NaN只会保存一个
  3. reduce判断上次回调函数的返回值是否includes当前值
  4. sort+相邻数据比较(sort 是调用 toString()方法,需要考虑不同数据类型时问题)

对象数组建议直接循环然后使用工具库(lodash)的深比较

reduce方法遍历对象数组
let obj = {}
let newArr = []
newArr = arr.reduce((prev, next) => {
  obj[next.name] ? ' ' : obj[next.name] = true && prev.push(next)
  return item
}, [])


sort+相邻数据比较:
Array.prototype.unique = function () {
  const newArray = [];
  this.sort();
  for (let i = 0; i < this.length; i++) {
    if (this[i] !== this[i + 1]) {
      newArray.push(this[i]);
    }
  }
  return newArray;
}

数组拆解:将多维数组转化为一维 flat: [1,[2,3]] --> [1, 2, 3]

  1. Array.prototype.toString()/join方法
  2. reduce
  3. while循环 + some
  4. 递归
  5. 调用ES6中的flat方法

juejin.cn/post/684490…

1. ary = ary.flat(Infinity);
2. 
Array.prototype.flat = function() {
console.log('sd',this.toString());//toString会直接解为0,1,2,3,4,5,6,7,item前面的加号是为了将字符串转为数值
    return this.toString().split(',').map(item => +item )

}
const newArr = function(arr){
   return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
}
-----------------------------
function flat(arr) {
  let result = [];
  let fn = function(arr) {
    for (let i = 0; i < arr.length; i++) {
      if (Array.isArray(arr[i])) {
        fn(arr[i]);
      } else {
        result.push(arr[i]);
      }
    }
  };
  fn(arr);
  return result;
}
let res = flat(arr);
console.log(res);
-----------------------------
let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
while(arr.some(item=>Array.isArray(item))){
    // 只要有数组类型 都会通过展开运算符进行降维
    arr = [].concat(...arr);
}


console.log(arr.flat());
console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]

找出Array中的最大元素

  1. 自己实现一个冒泡算法
  2. 利用Math的max方法
      var list = [1, 100, 23, 65, 43, 2, 9]
      console.log(Math.max.apply(null, list))
      console.log(Math.max(...list))
  1. 利用Array的sort方法先排序再取值

数组乱序:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.sort(function () {
    return Math.random() - 0.5;
});

[].map(parseInt)

注意:parseInt 可以有两个参数,第二个参数是进制数。 map 方法在调用 callback 函数时,会给它传递三个参数:当前正在遍历的元素,元素索引, 原数组本身。第三个参数 parseInt 会忽视,但第二个参数不会,也就是说:parseInt 把传过来的索引值当成进制数来使用,从而返回了 NaN

["1", "2", "3"].map(parseInt);
// 你可能觉的会是[1, 2, 3]
// 但实际的结果是 [1, NaN, NaN]
// 一个更简单的方式:
['1', '2', '3'].map(Number);               // [1, 2, 3]
// 与`parseInt` 不同,下面的结果会返回浮点数或指数:
['1.1', '2.2e2', '3e300'].map(Number);     // [1.1, 220, 3e+300]
['1', '2', '3'].map( str => parseInt(str) );

清空数组:直接将数组长度设置为0

数组转为对象:{ …arr }

去除假值:假值有:false、0、''、null、NaN、undefined

var mixedArr = [0, “blue”, “”, NaN, 9, true, undefined, “white”, false];
var trueArr = mixedArr.filter(Boolean);
console.log(trueArr); // returns [“blue”, 9, true, “white”]

克隆数组:

juejin.cn/post/684490…