数组

315 阅读6分钟

一、数组排序

1. sort  方法

sort():是对数组元素进行排序,默认排序顺序是 先将元素转换为字符串,然后再按照转换为字符串的各个字符的 Unicode(字符编码) 位点进行排序进行排序

sort(function(){}):可对数字排序

var arr = new Array('1','3','8','55','2','3','5','66');
arr = arr.sort();
console.log(arr.toString()) // 1,2,3,3,5,55,66,8

2. sort 降序、升序排列

let arr = [66, 55, 888,444, 222,  '111', 1114,'2222']
// 等价于 arr.sort((a, b)=>b-a)  
arr.sort(function(a,b){
    return a-b
})
console.log([...new Set(arr)]);  
// [66, 55, 888, 444, 222, '111', 1114, '2222']
 
/*
 * return b-a; —> 降序排序
 * return a-b; —> 升序排列
 */

3.倒叙 reverse()

var array=['我','喜','欢','你'];
array.reverse();  // ['你', '欢', '喜', '我']

4.冒泡排序

就是把一组乱序的数组排成一个整齐的数组,一种基础的排序算法

核心逻辑(正序):把数组中的前两个数据拿来比较,如果前面的数大于后面的数,那么就进行位置交换,如果前面的数没有后面的大,就不交换位置。

步骤:先遍历数组,让挨着的两个进行比较,如果前一个比后一个大,那么就把两个换个位置。 数组遍历一遍以后,最后的一个数字就是最大的那个。然后进行第二遍的遍历,还是按照之前的规则,第二大的数字就会跑到倒数第二的位置。以此类推,最后就会按照顺序把数组排好了。

/*
* 总共比较次数为arr.length-1次
* 每次的比较次数为arr.length-1次
* 依次递减
*/
var arr = [10, 20, 40, 60, 60, 0, 30]
 
var temp;//交换变量标识
 
// 两层for分别表示当前项与第二项
for(let i = 0; i < arr.length - 1; i++) {
 for(let j = 0; j < arr.length - 1; j++) {
 
  // 如果当前项大于第二项(后一项)则交换
  if(arr[j] > arr[j+1]) {
   temp = arr[j]
   arr[j] = arr[j+1];
   arr[j+1] = temp;
  }
 }
}
 
// 打印排序后的数组
console.log(arr)//[0, 10, 20, 30, 40, 60, 60]

5.希尔排序

希尔排序思路: 将无序数组分割成若干子序列,子序列不是逐段分割的,而是相隔特定增量的子序列,对各个子序列进行插入排序,然后再选择一个更小的增量,再将之前排序后的数组按这个增量分割成多个子序列,...,不断选择更小增量,直到增量为1时,再对序列进行一次插入排序,使序列最终成为有序序列,排序完成。

所以关键是确认一个有规律的逐渐递减的增量,这里的首选增量为n/2,每次增量为原先的1/2,直到增量为1

第二层循环为什么循环从 增量开始然后递增呢? 从增量开始,减去增量本身,得到的就是与该元素相差增量个的前一个元素 增量依次递增,每次减去增量本身,就依次得到了与该元素相差增量个的每一个元素

function shellSort(arr) {
    const len = arr.length;
    // 增量为每次递减二分之一的数
    for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        // 对以增量为间隔所形成的子序列进行插入排序
        // 为什么循环从 增量开始然后递增呢?
        // 从增量开始,减去增量本身,得到的就是与该元素相差增量个的前一个元素
        // 增量依次递增,每次减去增量本身,就依次得到了与该元素相差增量个的每一个元素
        for (let i = gap; i < len; i++) {
            // 得到与该元素相差增量个的前一个元素
            let j = i - gap;
            // 插入排序是要移动数组的,防止要插入的元素被覆盖掉
            let temp = arr[i];
            // 间隔增量个的前面的元素更大,就往后移动
            while (j >= 0 && temp < arr[j]) {
                arr[j + gap] = arr[j];
                j = j - gap;
            }
            // 循环结束,证明循环结束位置的元素比要插入的元素小
            // 所以循环结束位置的下一个位置就是要插入的位置
            arr[j + gap] = temp;
        }
    }
}
let list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
shellSort(list)
console.log(list); 
//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

一、数组扁平化

数组扁平化就是将一个多维数组转换为一个一维数组

实现思想

  1. 对数组的每一项进行遍历。
  2. 判断该项是否是数组。
  3. 如果该项不是数组则将其直接放进新数组。
  4. 是数组则回到1,继续迭代。
  5. 当数组遍历完成,返回这个新数组。

1. 使用数组 concat方法

var arr= [[0,0,1],[2,3,3],[4,4,5]];
var newArr = [];
for(var i=0;i<arr.length;i++){
     newArr=newArr.concat(arr[i])            
}
console.log(arr)  //[[0,0,1],[2,3,3],[4,4,5]];
console.log(newArr)  // [0, 0, 1, 2, 3, 3, 4, 4, 5]

2. 数组 join 方法

但是有一个缺点就是使数组每一项都变成了字符串

var arr=[1,[2,[[3,4],5],6]];
function getArr(arr){ 
   return arr.join().split(","); 
} 
console.log(getArr(arr));  // ['1', '2', '3', '4', '5', '6']

3. 递归

var arr = [1,[2,[[3,4],5],6]];
var newArr = [];
    
function fun(arr){
        for(var i=0;i<arr.length;i++){
            if(Array.isArray(arr[i])){
                fun(arr[i]);
            }else{
                newArr.push(arr[i]);
            }
        }
    }
fun(arr);
console.log(newArr);//[1, 2, 3, 4, 5, 6]

4. for in循环  递归

其实第四种跟第三种差不多,换用写法而已

var arr =[1,[2,[[3,4],5],6]];
var newArr=[]; 
function getArr(arr) { 
    for(var k in arr) { 
       if( arr[k] instanceof Array) {
               getArr  (arr[k]); 
     }else { newArr.push(arr[k]); 
   } 
  } 
  return newArr;
} 
console.log(getArr(arr));  

二、数组去重

1. 由对象组成的数组去重

var o = {};
[{name: '33'}, {name: '55'}].forEach(item => {
    o[item.name] = item;
})

Object.values(o)

2. 去重:遍历数组法

var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
var newArr = []
for (var i = 0; i < arr.length; i++) {
    if (newArr.indexOf(arr[i]) === -1) {
           newArr.push(arr[i])
     }
}
console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]

3. 去重:数组下标判断法

var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
var newArr = []
for (var i = 0; i < arr.length; i++) {
     if (arr.indexOf(arr[i]) === i) {
       newArr.push(arr[i])
     }
}
console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]

看 if 这里,在遍历 arr 的过程中,如果在 arr 数组里面找当前的值,返回的索引等于当前的循环里 面的 i 的话,那么证明这个值是第一次出现,所以推入到新数组里面

如果后面又遍历到了一个出现过的值,那也不会返回它的索引,indexof() 方法只返回找到的第一个值的索引,所以重复的都会被 pass 掉,只出现一次的值都被存入新数组中,也达到了去重的目的。

4. 去重:排序后相邻去除法

这种方法用到了 sort( ) 方法,代码如下:

 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
 arr.sort()
 var newArr = [arr[0]]
 for (var i = 1; i < arr.length; i++) {
    if (arr[i] !== newArr[newArr.length - 1]) {
       newArr.push(arr[i])
    }
 }
console.log(newArr) // 结果:[0, 2, 5, 6, 7, 8]

这种方法的思路是:

先用 sort() 方法把 arr 排序,那么排完序后,相同的一定是挨在一起的, 把它去掉就好了,

首先给新数组初始化一个 arr[0],因为我们要用它和 arr 数组进行比较

所以,for 循环里面 i 也是从 1 开始了,我们让遍历到的 arr 中的值和新数组最后一位进行比较,如果相等,则 pass 掉,不相等的,push 进来,

因为数组重新排序了,重复的都挨在一起,那么这就保证了重复 的这几个值只有第一个会被 push 进来,其余的都和新数组的被 push 进来的这个元素相等,会被 pass 掉,也达到了去重的效果。

5. 去重:利用ES6 Set去重

Set 数据结构,它类似于数组,其成员的值都是唯一的。 利用 Array.from 将 Set 结构转换成数组

function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];

console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

6. 去重:优化的遍历数组法

var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2, 8]
var newArr = []
for (var i = 0; i < arr.length; i++) {
      for (var j = i + 1; j < arr.length; j++) {
        if (arr[i] === arr[j]) {
          i++
          j = i
        }
      }
     newArr.push(arr[i])
   }
console.log(newArr) // 结果:[0, 5, 6, 7, 2, 8]

思路: 两层 for 循环,外面一层是控制遍历到的前一个 arr 中的元素,里面一层控制的是第一层访问到 的元素后面的元素,不断的从第 0 个开始,让第 0 个和他后面的元素比较,如果没有和这个元素相等的, 则证明没有重复,推入到新数组中存储起来,如果有和这个元素相等的,则 pass 掉它,直接进入下一次 循环。

从第 1 个开始,继续和它后面的元素进行比较,同上进行,一直循环到最后就是:不重复的 都被推入新数组里面了,而重复的前面的元素被 pass 掉了,只留下了最后面的一个元素,这个时候也就 不重复了,则推入新数组,过滤掉了所有重复的元素,达到了去重的目的。

7. 去重:数组遍历法

var arr = ['a', 'a', 'b', 'c', 'b', 'd', 'e', 'a']
var newArr = []
for (var i = 0; i < arr.length; i++) {
      var bl = true
      for (var j = 0; j < newArr.length; j++) {
        if (arr[i] === newArr[j]) {
          bl = false
          break
        }
     }
     if (bl) {
       newArr.push(arr[i])
     }
}
console.log(newArr) // 结果:["a", "b", "c", "d", "e"]

思路: 也是两层 for 循环,外层 for 循环控制的是 arr 数组的遍历,内层 for 循环控制的是新数组的 遍历,

从第 0 位开始,如果新数组中没有这个 arr 数组中遍历到的这个元素,那么状态变量 bl 的值还是 true,那么自然进入到了 if 中把这个值推入到新数组中,如果有这个元素,那么代表重复, 则把状态变量 bl 取值改为false,并且跳出当前循环,不会进入到 if 内部,而进入下一次 外层开始的循环。这样循环往复,最后也达到了去重的效果。

三. 不改变原数组的方法

1. slice (截取数组)

slice(start, end) 方法可从已有的数组中返回选定的元素,组成一个新的数组。

  • 如果不传参数,会返回原数组
  • 如果一个参数,从该参数表示的索引开始截取,直至数组结束,返回这个截取数组
  • 如果两个参数,从第一个参数对应的索引开始截取,到第二个参数对应的索引结束,start 包含,end 不包含
  var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"]
  var citrus = fruits.slice(1,3)
  console.log(citrus)  // ["Orange","Lemon"]

2. concat ( 联合数组 )

concat()方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

var hege = ["Cecilie", "Lone"];
var stale = ["Emil", "Tobias", "Linus"];
var kai = ["Robin"];
var children = hege.concat(stale,kai);

*children* 的输出结果: Cecilie,Lone,Emil,Tobias,Linus,Robin

concat()不会进行去重,但是我们可以封装一个合并数组并去重的方法, 使用函数调用参数,来对数组进行 concat 合并处理指向 apply 调用 this 执行函数

function combine(){
     //这里是没有去重,是进行合并数组
     let arr=[].concat.apply([],arguments); 
     //set 可以过滤掉重复的数字
     return Array.from(new Set(arr)); 
}
var m =[1,2,3,2],
    n=[2,3,4];
console.log(combine(m,n))  // [1, 2, 3, 4]

3. join (将数组转换成字符串)

join() 方法用于把数组中的所有元素转换一个字符串。 元素是通过指定的分隔符进行分隔的。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
var energy = fruits.join();    // Banana,Orange,Apple,Mango

4. for 循坏

for 方法跳出循环-----break 跳出循环 ; continue 跳出当次循环

 var arr=[1,3,5,7,9];
    var id = 5;
    for(var i=0;i<arr.length;i++){
      if(arr[i]===1)continue;
      console.log(arr[i])
      if(arr[i]===id){
        break;
      }
}

5. forEach 循坏

forEach方法跳出循环----通过抛出异常的方式 try...catch...跳出循环; 通过 return 跳过当次循环

var arr=[1,3,5,7,9];
var id = 5;
try{
  arr.forEach(function(curItem,i){
    if(curItem===1)return;
    console.log(curItem);
    if(curItem===id){
      throw Error();
    }
  })
}catch(e){
  console.log(e)
}

6. for和 forEach 的区别

1.forEach() 不能使用 break 和 continue 这两个关键字,它实现 break 效果可以通过抛出异常的方式,实现 continue 的效果可以直接使用 return

2.forEach 的优势就是,它传入一个函数,因此形成了一个作用域,它内部所定义的变量不会像 for 循环一样污染全局变量

3.forEach() 本身无法跳出循环,必须遍历所有的数据才能结束

4.for 方法跳出循环-----break跳出循环 continue跳出当次循环

5. map 与 foreach 的区别

1、相同点

  • 都是循环遍历数组中的每一项。
  • 每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组)。
  • 匿名函数中的 this 都是指向 window。
  • 只能遍历数组。

2、不同点

  • map( ) 会分配内存空间存储新数组并返回,forEach() 不会返回数据
  • forEach( ) 允许 callback 更改原始数组的元素。map() 返回新的数组

6. map ( )

map( )返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值

var array1 = [1,4,9,16];
const map1 = array1.map(x => x *2);
console.log(map1);  // [2,8,18,32]

7. filter ()

会创建一个新数组,原数组的每个元素传入回调函数中,回调函数中有 return 返回值,若返回值为 true,这个元素保存到新数组中;若返回值为 false,则该元素不保存到新数组中

注意:filter() 不会对空数组进行检测;不会改变原始数组

let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
let res = nums.filter((num) => {
  return num > 5;
});
 
console.log(res);  // [6, 7, 8, 9, 10]

8. reduce()

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值

let 结果 = 数组.reduct(累加器, 初始化值)
//累加器
(累加结果, 当前值, 当前索引)=>{

	return 处理结果
}


// 累加和
// reduce 案例1:累加和
let arr = [1,2,3,4,5]
let s = arr.reduce( (sum,current,index)=>{
    return sum + current
} , 0)
console.info(s)


//案例2:对象数组的累加和
let arr = [
    {
        name: 'jack',
        count: 10
    },
    {
        name: 'rose',
        count: 20
    }
]
let s = arr.reduce((sum,current,index)=>{
    return sum + current.count
},0);
console.info(s)



//案例2:对象数组的累加和
let arr = [
    {
        name: 'jack',
        count: 10
    },
    {
        name: 'rose',
        count: 20
    }
]
let s = arr.reduce((sum,current,index)=>{
    return sum + current.count
},0);
console.info(s)


//案例3:
let arr = [
    {name: 'jack',course:'语文',count: 100},
    {name: 'jack',course:'英语',count: 100},
    {name: 'jack',course:'数学',count: 100},
    {name: 'rose',course:'语文',count: 50},
    {name: 'rose',course:'英语',count: 50},
    {name: 'rose',course:'数学',count: 50}
]
//reduce
let s2 = arr.reduce((obj,current,index)=>{
    let name = current.name
    let count = current.count
    // 获得obj对应名称值  {'jack':100} --》 100
    let oldCount = obj[name]
    // oldCount 如果是 undefined,表示此名称第一次出现
    if(oldCount){
        obj[name] = oldCount + count
    } else {
        //第一次赋值
        obj[name] = count
    }
    console.info(`之前累加值:${oldCount},当前值:${count},最终结果:${JSON.stringify(obj)}`)

    return obj
} , {})


//结果:{'jack': 300, 'rose': 150}
/*没有数据:{}
    {'jack': 100}
    {'jack': 200}
    {'jack': 300}
    {'jack': 300,'rose':50}
    {'jack': 300,'rose':100}
    {'jack': 300,'rose':150}

*/
console.info(s2)

9. some()

检测数组中是否含有某一个值,如果数组中有任意一个元素满足给定的条件,结果为 true, 否则为 false

var arr = [32, 33, 16, 40]
var newArr = arr.some((item,index,arr)=>{
    return item >= 18 
})
console.log(arr) // [32, 33, 16, 40]
console.log(newArr) // true

四. 改变原数组的方法

1. push() unshift() 返回数组的长度

var arr = [1,2,3,4]
var a = arr.unshift(9,8,7)
console.log('a='+a+' arr='+arr) // a=7 arr=9,8,7,1,2,3,4

2. pop() 方法用于删除并返回数组的最后一个元素。

var arr = [4,3,2,3]
var a = arr.pop()
console.log('a='+a+' arr='+arr) // a=3 arr=4,3,2

3. shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。

let arr = [1, 2,3]
let a = arr.shift();
console.log(a);   // 1
console.log(arr)  //  [2,3]

4. reverse() 方法用于颠倒数组中元素的顺序。

var fruits = ["Banana", "Orange", "Apple", "Mango"]
fruits.reverse()
console.log(fruits) // ["Mango", "Apple", "Orange", "Banana"]

5. sort () 排序数组

let arr = ["10", "5", "40", "25", "1000", "1"];
function sortNumber (a, b) {
    return a - b
}
let a = arr.sort();
let b = arr.sort(sortNumber);
console.log(a)
console.log(b)

6. splice 截取数组的同时,还会插入数据,返回的值为截取出来的元素

// 6. array.splice(index,howmany,item1,…,itemX)
// a.没有参数,返回空数组,原数组不变
// b.一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的数组,
//   原数组改变;
// c.两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,
原数组改变;
// d.三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。

var fruits = ["Banana", "Orange", "Apple", "Mango"]
var a = fruits.splice(1,2,'Peel')
console.log(a) // ["Orange", "Apple"]
console.log(fruits)  // ["Banana", "Peel", "Mango"]

五. ES5 新增的一些数组方法

1. indexOf(searchvalue,start)

字符串,数组都可适用,此方法可返回某个指定的字符串值在字符串中首次出现的位置 若一个参数,返回这个参数在数组里面的索引值,如果参数不在操作的数组中,则返回 -1。

var arr = [1,2,3,4];
arr.indexOf(1) // 0
arr.indexOf(5) // -1 

2. forEach(function(item, index, arr))

数组遍历,且只能够遍历数组,不接受返回值或返回值为 undefined,单纯对数组进行循环如果数组中的值为 empty, 则不会执行回调函数

var arr = [1,2,3,4,5];
arr.forEach((item,index,arr)=>{
   // item: 遍历的数组内容
   // index: 对应的数组索引
   // arr: 数组本身
})

3. array.map(function(item,index,arr)) (不改变原数组)

数组的遍历,用来接收一个返回值,创建一个新数组

var arr = [1,2,3,4,5,6]
var newArr = arr.map(function(item,index,arr){
    return item * 2
})
console.log(arr)  // [1, 2, 3, 4, 5, 6]
console.log(newArr) // [2, 4, 6, 8, 10, 12]

4. array.filter(function(item,index,arr)) (不改变原数组)

过滤出一些符合条件的元素,返回一个新数组 (返回为 true,该元素就进新数组,false反之)

var arr = [32, 33, 16, 40]
var newArr = arr.filter((item,index,arr)=>{
    return item >=18 
})
console.log(arr) // [32, 33, 16, 40]
console.log(newArr) // [32, 33, 40]

5. array.some(function(currentValue,index,arr)) (不改变原数组)

检测数组中是否含有某一个值,如果数组中有任意一个元素满足给定的条件,结果为 true,否则为false

var arr = [32, 33, 16, 40]
var newArr = arr.some((item,index,arr)=>{
    return item >= 18 
})
console.log(arr) // [32, 33, 16, 40]
console.log(newArr) // true

6. array.every(function(currentValue,index,arr)) (不改变原数组)

方法用于检测数组所有元素是否都符合指定条件(通过函数提供),返回一个布尔值,结果为 true 或 false

var arr = [32, 33, 16, 40]
var newArr = arr.every((item,index,arr)=>{
    return item >=18 
})
console.log(arr) // [32, 33, 16, 40]
console.log(newArr) // false

7. array.reduce(function(total, currentValue, currentIndex, arr))

对数组中的所有元素调用指定的回调函数,该回调函数的返回值为累计结果。且把返回值作为下一次回调函数的参数。

var arr = [1,2,3,4,5,6,7,8,9];
var res = arr.reduce((pre,next,index,arr1)=>{
    console.log("pre:"+pre)    // 前一个的值 pre: 0 1 3 6 10 15 21 28 36
    console.log("next:"+next)  // 后一个的值 next:1 2 3 4 5 6 7 8 9
    console.log('arr1:'+arr1)
    return pre+next
})
console.log("arr:"+arr)  // arr:1,2,3,4,5,6,7,8,9
console.log("res:"+res)  // res:45

六. ES6 数组方法

1. includes( )

includes( ) 检测数组中是否包含一个值。

[1, 2, 3].includes(1)      // true
[1, 2, 3].includes(1, 2)   // false  从位置索引 2 可以查找是否包含1的值
[1, NaN, 3].includes(NaN)  // true

2. Array.from(arrayLike[, mapFn[, thisArg]]) 将类数组转换为数组

let array = {
    0: 'name', 
    1: 'age',
    2: 'sex',
    3: ['user1','user2','user3'],
    'length': 4
}
let arr = Array.from(array )
console.log(arr) // ['name','age','sex',['user1','user2','user3']]

Array.from(str) 将字符串转换为数组
let  str = 'hello world!';
console.log(Array.from(str)) // ["h", "e", "l", "l", "o", " ", "w", "o", "r", 
"l", "d", "!"]

3. Array.of()

数组创建,将参数中所有值作为元素形成数组,如果参数为空,则返回一个空数组

console.log(Array.of()) // []
console.log(Array.of(1, 2, 3, 4)) // [1, 2, 3, 4]
console.log(Array.of(1, '2', true)) // [1, '2', true] 

4. find()

查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素

let arr = Array.of(1, 2, 3, 4)
console.log(arr.find(item => item > 2)) // 3 
console.log([, 1].find(n => true)) // undefined

5. findIndex()

查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。

var arr = Array.of(1, 2, 1, 3)
console.log(arr.findIndex(item => item == 1)); // 0
console.log([, 1].findIndex(n => true)); // 0 

6. fill()

将一定范围索引的数组元素内容填充为单个指定的值。

// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
var arr1 = Array.of(1, 2, 3, 4)
var arr2 = Array.of(1, 2, 3, 4)
console.log(arr1.fill(0,1))   // [1, 0, 0, 0]
console.log(arr2.fill(0,1,3)) // [1, 0, 0, 4]

7. … 扩展运算符

把数组或对象展开一系列用逗号 (,) 隔开的值
var arr = [1, 2],
arr1 = [...arr] 
console.log(arr1) // [1, 2]

// 数组含空位
var arr2 = [1, , 3],
arr3 = [...arr2]
console.log(arr3)  // [1, undefined, 3]

// 合并数组
console.log([...[1, 2],...[3, 4]]); // [1, 2, 3, 4]

// es6的扩展运算符能将二维数组变为一维
[].concat(...[1, 2, 3, [4, 5]]); // [1, 2, 3, 4, 5]

七、数组其他相关

1. 数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少

数组可以直接根据索引取的对应的元素,所以不管取哪个位置的元素的时间消耗时间几乎一致,差异可以忽略不计

2. for…in 与 for…of 的区别

  1. 都可以遍历数组,for in 返回数组的下标(key);for of 返回数组的元素;
  const arr1 = ['a', 'b', 'c'];
  for (let i in arr1) {
	console.log(i)  // 0, 1, 2
  }
	  
  const arr2 = ['a', 'b', 'c'];
  for (let i of arr2) {
    console.log(i) // a, b, c
  }
  1. 遍历对象 for in 遍历获取对象 key 值; for of 报错;
    const json = {"a": 1, "b": 2, "c": 3};
    for (let i in json) {
      console.log(i) // a, b, c
    }
    for (let i of json) {
      console.log(i)     // 数据格式错误
    }
  1. 给数组手动添加属性, for in 循环可以遍历出 name 这个添加属性的键名
    const arr3 = ['a', 'b'];
    arr3.name = 'c';
    for (let i in arr3) {
        console.log(i) // 0, 1, name
    }
  1. for in 的特点
  • for … in 循环返回的值都是数据结构的 键值名。
  • 遍历对象返回的对象的key值;
  • 遍历数组返回的数组的下标(key)。
  • for … in 循环不仅可以遍历数字键名,还会遍历原型上的key值和手动添加的其他键;
  • 特别情况下, for … in 循环会以任意的顺序遍历键名
  • 总结一句: for in 循环特别适合遍历对象。
  1. for of 特点:
  • for of 循环用来获取一对键值对中的值,而 for in 获取的是 键名
  • 一个数据结构只要部署了 Symbol.iterator 属性, 就被视为具有 iterator 接口, 就可以使用 for of循环。 例 3 这个对象,没有 Symbol.iterator这个属性,所以使用 for of会报json is not iterable
  • for of 不同与 forEach, 它可以与 break、continue 和 return 配合使用, 也就是说 for of 循环可以随时退出循环。
  1. 我也想让对象可以使用 for of 循环怎么办? 使用 Object.keys() 获取对象的 key 值集合后, 再使用 for of
   const obj = {a: 'x', b: 'y'};
   for (let i of Object.keys(obj)) {
      console.log(i)   // a, b
    }

3. 找出数组中出现次数最多的元素

方法 1. 利用键值对存储

实现方法1的主要思想是利用键值对存储,我们可以分解为两个步骤。

  1. 定义一个对象,在遍历数组的时候,将数组元素作为对象的键,将出现的次数作为值
  2. 获取键值对后进行遍历,获取值最大的那个元素,返回后即可得到结果。通过以上的思想,我们可以得到以下实现代码。

因为方法1会 首先对数组进行遍历,然后对对象进行遍历,在实现效率上比较低下,不推荐使用。

image.png

方法 2.主要思想同方法1,

不过是方法1的优化,将方法1中的两次遍历缩减为一次遍历,将值的判断放在同一个遍历中。

image.png

3.借助数组Array的reduce方法

首先我们来看看reduce方法的使用方法,它的语法如下。

arr.reduce([callback, initialValue])reduce方法接收的第一个参数为函数,操作数组中的每个元素。该函数接收4个参数,每个参数的含义如下。

  1. 第一个参数表示上一次执行结果的回调,或者第二个参数提供的初始值
  2. 当前处理的元素值
  3. 当前处理元素的索引
  4. 处理的数组

reduce方法接收的第二个参数为提供处理元素的初始值,与上面的第一个参数有关。

在了解 reduce 方法后,我们可以直接看看下面的代码。

image.png

4. 修改后端返回的树结构的字段名

后端给的树结构是分级的。但是字段跟组件给的对不上。 blog.csdn.net/m0_51434664…

第一种方法:修改返回的树的字段名,使用replace来筛选所有的字段,这个地方要加g,g 指的是全局,就是不管你的树有几级,他都会给你找出来并修改,要不然只会修改第一级的。简单高效。不用递归, 代码如下

var replaceStr = JSON.parse(JSON.stringify(datas.childs).replace(/"cateId"/g, '"value"').replace(/"childs"/g, '"children"'))

第二种方法:树结构组件可以自定义节点的字段,让组件去匹配你的树结构的字段

image.png

注意:级联数组去掉最后一层空数组 childern,以防最后一层显示空白。

// 递归判断列表,把最后的children设为undefined
getTreeData(data) {
  for (var i = 0; i < data.length; i++) {
      if (data[i].node.length < 1) {
          // children若为空数组,则将children设为undefined
          data[i].node = undefined;
      } else {
          // children若不为空数组,则继续 递归调用 本方法
          this.getTreeData(data[i].node);
      }
  }
  return data;
},

5. 根据 相同的 key 值把多个数组合并

需求如下 ,需要把 arr1 和 arr2 根据相同的 code 值,并且其中的 cout 值进行加以区分, 转化成 arr3 的形式

# 返回数据
let arr1 = [
  { name: "无", count: 1, code: 0 },
  { name: "全新现车", count: 2, code: 1 },
  { name: "客户订单", count: 3, code: 10 },
  { name: "试驾车", count: 4, code: 7 },
  { name: "展车", count: 5, code: 8 },
];
let arr2 = [
  { name: "无", count: 67, code: 0 },
  { name: "全新现车", count: 7, code: 1 },
  { name: "客户订单", count: 40, code: 10 },
  { name: "试驾车", count: 22, code: 7 },
  { name: "展车", count: 1, code: 8 },
];

// # 期望数据
let arr3 = [
  { name: "无", arr1Count: 1, arr2Count: 67, code: 0 },
  { name: "全新现车", arr1Count: 2, arr2Count: 7, code: 1 },
  { name: "客户订单", arr1Count: 3, arr2Count: 40, code: 10 },
  { name: "试驾车", arr1Count: 4, arr2Count: 22, code: 7 },
  { name: "展车", arr1Count: 5, arr2Count: 1, code: 8 },
];

实现代码

// 数组对其中的 count 进行别名命名
function getNewList(key, list) {
  let keyStr = `${key}Count`;
  return list.map((ele) => {
    let name = ele.name;
    let code = ele.code;
    return {
      [keyStr]: ele.count,
      code,
      name,
    };
  });
}
// 根据 指定的 key 进行合并数据
function arrMerge(arr1, arr2 , key) {
  const combined = arr1.map((item1) => {
    arr2.map((item2) => {
      if (item1[key] === item2[key]) {
        Object.assign(item1, item2);
      }
    });
    return item1;
  });
  return combined;
}


const newArr1 = getNewList(  "arr1", arr1 );
const newArr2 = getNewList("arr2",arr2);
let arr3 = arrMerge(newArr1, newArr2,  "code");

arr3 打印输出结果如下:

image.png

6. JS 数组对象根据某一相同 key 合并成新的数组

需求是将具有相同 “school” 的对象合并为新的数组

例子:

# 原始数据
let arr = [
  { name: '小明',age: 20,school: '清华' },
  { name: '小红',age: 21,school: '清华' },
  { name: '小白',age: 18,school: '北大' },
  { name: '小黄',age: 19,school: '北大' },
  { name: '小浪',age: 21,school: '哈佛' },
]

# 期望数据
let data = [
  {
    school: "清华",
    children: [
      { name: "小明", age: 20, school: "清华" },
      { name: "小红", age: 21, school: "清华" },
    ]
  },
  {
    school: "北大",
    children: [
      { name: "小白", age: 18, school: "北大" },
      { name: "小黄", age: 19, school: "北大" },
    ]
  },
  {
    school: "哈佛",
    children: [
      { name: "小浪", age: 21, school: "哈佛" },
    ]
  },
]

实现代码如下:

function handlerDatas(arr){
  let obj = {};
  arr.forEach((item, index) => {
      let { school } = item;
      if (!obj[school]) {
          obj[school] = {
              school,
              children: []
          }
      }
      obj[school].children.push(item);
  });
  let data = Object.values(obj); // 最终输出
  return data
}

7. 扁平数组转 tree 结构

const flatArr = [
  { id: '01', parentId: 0, name: '节点1' },
  { id: '011', parentId: '01', name: '节点1-1' },
  { id: '0111', parentId: '011', name: '节点1-1-1' },
  { id: '02', parentId: 0, name: '节点2' },
  { id: '022', parentId: '02', name: '节点2-2' },
  { id: '023', parentId: '02', name: '节点2-3' },
  { id: '0222', parentId: '022', name: '节点2-2-2' },
  { id: '03', parentId: 0, name: '节点3' },
]

function getData (arr) {
  // 利用两层filter实现
  let data = arr.filter(item => {
    item.children = arr.filter(e => {
      return item.id === e.parentId
    })
    return !item.parentId
  })

  return data
}
const res = getData(flatArr)
console.log('res', res)

打印结果如下:

image.png