【常考前端手写题】数组篇

131 阅读24分钟

一、理论知识

数组基础知识总结

二、实现数组基本数据结构

/**
 * 基本数组结构
 */
class BasicArray {
    constructor() {
        this.length = 0;
        this.data = {};
    }

    /**
     * 末尾增加元素
     * @param {*} item 
     */
    add(item) {
        this.data[this.length] = item;
        this.length++;
    }

    /**
     * 任意位置插入元素
     * @param {*} index 
     * @param {*} item 
     */
    insert(index, item) {
        if (index < 0) index = 0;
        if (index > this.length) index = this.length;
        // 从最后一个开始移动,让出i的位置
        if (index >= 0 && index <= this.length) {
            for (let i = this.length; i > index; i--) {
                this.data[i] = this.data[i - 1];
            }
            this.data[index] = item;
        } 
        this.length++;
    }

    /**
     * 删除指定位置元素
     * @param {*} index 
     * @returns 
     */
    delete(index) {
        const deleted = this.data[index];
        // 用i后面的元素覆盖之前的元素
        for (let i = index; i < this.length - 1; i++) {
            this.data[i] = this.data[i + 1];
        }
        delete this.data[this.length - 1];
        this.length--;
        return deleted;
    }

    /**
     * 更新元素
     * @param {*} index 
     * @param {*} item 
     */
    update(index, item) {
        this.data[index] = item;
    }

    /**
     * 获取数组长度
     * @returns 
     */
    getLength() {
        return this.length;
    }

    /**
     * 获取元素
     * @param {*} index 
     * @returns 
     */
    get(index) {
        return this.data[index];
    }

    /**
     * 打印
     */
    print() {
        const result = [];
        for (let i = 0; i < this.length; i++) {
            result.push(this.data[i]);
        }
        console.log(result);
    }
}



// // 基本数组使用示例
// const myArray = new BasicArray();
// myArray.insert(0, 'a');
// myArray.insert(1, 'b');
// myArray.insert(2, 'c');
// myArray.print(); // ['a', 'b', 'c']

// myArray.update(1, 'x');
// myArray.print(); // ['a', 'x', 'c']

// myArray.delete(0);
// myArray.print(); // ['x', 'c']

// console.log(myArray.getLength()); // 2

module.exports = BasicArray;

三、实现数组的方法

1.集合类

forEach

1.基本使用

语法

array.forEach(function(currentValue, index, curArr), thisValue)

定义:用于调用数组的每个元素,并将元素传递给回调函数,没有返回值,原数组不变。

返回值:无

参数callback(currentValue,index,curArr),thisValue

注意

  • forEach() 对于空数组是不会执行回调函数的。
  • 无法中途退出循环,只能用return退出本次回调,进行下一次回调。
  • 它总是返回 undefined值,即使你return了一个值。
  • 对于已在迭代过程中删除的元素,或者空元素会跳过回调函数
  • 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
  • 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。
// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// curArr 数组本身
// 无返回值
arrObj.forEach(function(item,index,curArr) {
  console.log(item);
});

let arr = [1, 2, 3, 4];
arr.forEach(item => {
    item = item * 2;
});
console.log(arr); // [1, 2, 3, 4]   原数组保持不变

2.实现

myForEach(callback, thisValue) {
  const array = thisValue ? thisValue.data : this.data;
  // `forEach()` 对于空数组是不会执行回调函数的。
  if (array.length === 0) return;
  // 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
  for (let i = 0; i < array.length; i++) {
    // 它总是返回 undefined值,即使你return了一个值。
    // 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。
    array[i] && callback(array[i], i, array);
  }
}

map

1.基本使用

语法

array.map(function(currentValue, index, curArr), thisValue)

定义:按照原始数组元素顺序依次处理元素。

返回值:方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,并没有改变原来的数组。

参数callback(currentValue,index,curArr),thisValue

注意:map() 不会对空数组进行检测。

// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// curArr 数组本身
// 有返回值
// newArr 新返回的数组
// 数组元素个数不变,但是按照一定的条件转换
arrObj.map(function(item,index,curArr) {
  return item*2;
});

let arr = [1, 2, 3, 4];
let newArr = arr.map(item => {
    return item * 2;
});
console.log(arr); // [1, 2, 3, 4]   原数组保持不变
console.log(newArr); // [2, 4, 6, 8] 返回新数组

2.实现

myMap(callback, thisValue) {
  const result = [];
  const array = thisValue ? thisValue.data : this.data;
  if (array.length === 0) return [];
  for (let i = 0; i < array.length; i++) {
    result[i] = callback(array[i], i, array);
  }
  // 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,并没有改变原来的数组。
  return result;
}

filter

1.基本使用

语法

array.filter(function(currentValue,index,curArr),thisValue)

定义:将所有元素进行判断,将满足条件的元素作为一个新的数组返回, 原数组不变。

返回值:满足条件的元素作为一个新的数组,并没有改变原来的数组。

参数callback(currentValue,index,curArr),thisValue

注意filter() 不会对空数组进行检测。

// arrObj 需要遍历的数组
// item 遍历出的每一个元素
// index 元素对应的下标
// curArr 数组本身
// 有返回值,返回满足某个条件的元素构成的数组
// 数组元素个数可能改变,按照一定的条件返回
arrObj.filter(function(item,index,curArr) {
	return item < 3;
});
let arr = [1, 2, 3, 4];
let newArr = arr.filter(item => {
    return item < 3;
});
console.log(arr); // [1, 2, 3, 4]   原数组保持不变
console.log(newArr); // [1, 2]   返回新数组 

2.实现

用push避免有空位

myFilter(callback, thisValue) {
  const result = [];
  const array = thisValue ? thisValue.data : this.data;
  if (array.length === 0) return [];
  for (let i = 0; i < array.length; i++) {
    if (callback(array[i], i, array)) result.push(array[i]);
  }
  return result;
}

reduce

1.基本使用

语法

array.reduce(function(pre, currentValue, currentIndex, arr), initialValue)

定义:将数组中的所有项根据回调函数累加|累乘

返回值:boolean

参数:两个参数

  • 第一个为回调函数
    • 初始值或计算结束后的返回值
    • 当前元素
    • 当前元素的索引
    • 当前元素所属的数组对象
  • 第二个为初始赋值
    • 初始值,非必传

注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。

//未赋初始值
//index是从1开始的,第一次的prev的值是数组的第一个值。数组长度是4,循环3次。
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function (prev, cur, index, arr) {
  console.log(prev, cur, index);
  return prev + cur;
});
console.log(arr, sum);
//打印结果  
1 2 1  
3 3 2  
6 4 3  
[ 1, 2, 3, 4 ] 10

//赋初始值
这个例子index是从0开始的,第一次的prev的值是我们设置的初始值0,数组长度是4,reduce函数循环4次。
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function (prev, cur, index, arr) {
  console.log(prev, cur, index);
  return prev + cur;
}, 0);
console.log(arr, sum);
//打印结果  
0 1 0  
1 2 1  
3 3 2  
6 4 3  
[ 1, 2, 3, 4 ] 10

2.实现

myReduce(callback, initValue, thisValue) {
  const array = thisValue ? thisValue.data : this.data;
  // 如果传递初始值,从0开始
  let start = 0;
  let acc = initValue;
  if (array.length === 0) return acc;
  // 如果不传递初始值,从1开始
  if (initValue === undefined) {
    acc = array[0];
    start = 1;
  }
  for (let i = start; i < array.length; i++) {
    acc = callback(acc, array[i], i, array);
  }
  return acc;
}

完整代码

const BasicArray = require("../00实现数组结构/index");
class CollectArray extends BasicArray {
  initArr(arr) {
    this.data = arr;
  }

  myForEach(callback, thisValue) {
    const array = thisValue ? thisValue.data : this.data;
    // `forEach()` 对于空数组是不会执行回调函数的。
    if (array.length === 0) return;
    // 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
    for (let i = 0; i < array.length; i++) {
      // 它总是返回 undefined值,即使你return了一个值。
      // 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。
      array[i] && callback(array[i], i, array);
    }
  }

  myMap(callback, thisValue) {
    const result = [];
    const array = thisValue ? thisValue.data : this.data;
    if (array.length === 0) return [];
    for (let i = 0; i < array.length; i++) {
      result[i] = callback(array[i], i, array);
    }
    // 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,并没有改变原来的数组。
    return result;
  }

  myFilter(callback, thisValue) {
    const result = [];
    const array = thisValue ? thisValue.data : this.data;
    if (array.length === 0) return [];
    for (let i = 0; i < array.length; i++) {
      if (callback(array[i], i, array)) result.push(array[i]);
    }
    return result;
  }

  myReduce(callback, initValue, thisValue) {
    const array = thisValue ? thisValue.data : this.data;
    // 如果传递初始值,从0开始
    let start = 0;
    let acc = initValue;
    if (array.length === 0) return acc;
    // 如果不传递初始值,从1开始
    if (initValue === undefined) {
      acc = array[0];
      start = 1;
    }
    for (let i = start; i < array.length; i++) {
      acc = callback(acc, array[i], i, array);
    }
    return acc;
  }
}

const array = new CollectArray();
// 初始化一个数组
array.initArr([1, 2, 3, 4, 5, 6, 7, 8]);

// forEach
array.logHelper("forEach", array.getArray(), () => {
  array.myForEach((item, index, array) => {
    // console.log("✅ ~ item:", item);
  });
});

// map
array.logHelper("map", array.getArray(), () => {
  return array.myMap((item, index, array) => {
    return item * 2;
  });
});

// filter
array.logHelper("filter", array.getArray(), () => {
  return array.myFilter((item, index, array) => {
    return item < 4;
  });
});

// reduce
array.logHelper("reduce", array.getArray(), () => {
  return array.myReduce((pre, item, index, array) => {
    return pre * item;
  }, 0);
});

2.检索类

findIndex

1.基本使用

语法

array.findIndex(function(currentValue, index, curArr), thisValue)

定义findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。如果找到这样的元素,findIndex会立即返回该元素的索引。如果回调从不返回真值,或者数组的length0,则findIndex返回-1

返回值:number

参数:callback(currentValue,index,curArr),thisValue

用对象的属性查找数组里的对象。

 /*对象,取出满足要求的下标*/
const nameArr=[
    {id:1,userName:"a",age:16},
    {id:2,userName:"b",age:18},
    {id:3,userName:"c",age:26 },
    {id:4,userName:"d",age:18}
];
/*满足条件,返回下标位置1*/
var i1=nameArr.findIndex((value)=>value.age==18);
console.log(i1);
/*没有满足条件的,返回-1*/
var i2=nameArr.findIndex((value)=>value.age==168);
console.log(i2);

2.实现

myFindIndex(callback, thisValue) {
  const arr = thisValue ? thisValue.data : this.data;
  if (arr.length === 0) return -1;
  for (let i = 0; i < arr.length; i++) {
    if (callback(arr[i], i, arr)) {
      return i;
    }
  }
  return -1;
}

.find

1.基本使用

语法

array.find(function(currentValue,index,curArr),thisValue)

定义:查找数组中符合条件的元素,当数组中的元素在测试条件时返回true,find()返回符合条件的元素,之后的值不会再执行函数;如果没有符合条件的元素则返回undefined,原数组不变。

返回值:满足条件的元素,find()返回符合条件的元素,之后的值不会再执行函数;如果没有符合条件的元素则返回undefined,原数组不变。

参数:callback(currentValue,index,curArr),thisValue

注意:find() 对于空数组,函数是不会执行的。

let arr = [1, 2, 3, 4];
let newArr1 = arr.find(item => {
    return item > 2;
});
let newArr2 = arr.find(item => {
    return item > 5;
});
console.log(arr); // [1, 2, 3, 4]   原数组保持不变
console.log(newArr1); // 3  返回第一个符合条件的元素
console.log(newArr2); // undefined  没有符合条件的元素则返回undefined 

2.实现

myFind(callback, thisValue) {
  const arr = thisValue ? thisValue.data : this.data;
  if (arr.length === 0) return;
  for (let i = 0; i < arr.length; i++) {
    if (callback(arr[i], i, arr)) {
      return arr[i];
    }
  }
  return undefined;
}

.indexOf

1.基本使用

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

返回值:number

参数:两个参数searchElement,fromIndex (可选)

  • 第一个参数是‘要查找的元素’
  • 第二个参数是‘开始查找的索引位置’
  • 如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。
  • 如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。
  • 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0。
var array = [2, 5, 9];
array.indexOf(2);     // 0
array.indexOf(7);     // -1
array.indexOf(9, 2);  // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0

2.实现

myIndexOf(elem, startIndex) {
  const arr = this.data;
  const arrLength = arr.length;
  let startFindIndex = startIndex < 0 ? arrLength + startIndex : startIndex;
  if (startFindIndex > arrLength || arrLength === 0) return -1;
  if (startFindIndex < -arrLength) startFindIndex = 0;
  for (let i = startFindIndex; i < arrLength; i++) {
    if (arr[i] === elem) {
      return i;
    }
  }
  return -1;
}

.lastIndexOf

1.基本使用

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

返回值:number

参数:两个参数searchElement,fromIndex (可选) 从数组的后面向前查找,从 fromIndex 处开始。

  • 第一个参数是‘被查找的元素’。
  • 第二个参数是‘从此位置开始逆向查找’,默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。
    • 如果该值大于或等于数组的长度,则整个数组会被查找。
    • 如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。
    • 如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。
var array = [2, 5, 9, 2];
array.lastIndexOf(2);      // 3
array.lastIndexOf(7);      // -1
array.lastIndexOf(2, 3);   // 3
array.lastIndexOf(2, 2);   // 0
array.lastIndexOf(2, -2);  // 0
array.lastIndexOf(2, -1);  // 3

2.实现

myLastIndexOf(elem, startIndex) {
  const arr = this.data;
  const arrLength = arr.length;
  let startFindIndex = startIndex < 0 ? arrLength + startIndex : startIndex;
  if (startFindIndex > arrLength) startFindIndex = arrLength;
  if (startFindIndex < -arrLength) return -1;
  for (let i = startFindIndex; i >= 0; i--) {
    if (arr[i] === elem) return i;
  }
  return -1;
}

.every

1.基本使用

  • 语法array.every(function(currentValue, index, arr), thisValue)
  • 定义:测试一个数组内的所有元素是否都能通过某个指定函数的测试
  • 返回值:boolean
  • 参数:callback(currentValue,index,array),thisValue
    • thisValue:可选,执行 callback 时使用的 this 值。
    • elem 正在处理的元素
    • index 可选 正在处理元素的index
    • arr 可选,被遍历的数组本身。 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
function isBigEnough(element, index, array) {
  return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough);   // false
[12, 54, 18, 130, 44].every(isBigEnough); // true

2.实现

myEvery(callback, thisValue) {
  const arr = thisValue ? thisValue.data : this.data;
  if (arr.length === 0) return false;

  for (let i = 0; i < arr.length; i++) {
    if (!callback(arr[i], i, arr)) {
      return false;
    }
  }
  return true;
}

.some

1.基本使用

  • 语法array.some(function(currentValue,index,arr),thisValue)

  • 定义:测试数组中是不是至少有1个元素通过了被提供的函数测试

  • 返回值:boolean

  • 参数

    :callback(currentValue, index, array),thisValue

    • thisValue:可选,执行 callback 时使用的 this 值。
    • elem 正在处理的元素
    • index 可选 正在处理元素的index
    • arr 可选,被遍历的数组本身。
function isBiggerThan10(element, index, array) {  
    return element > 10;  
}
[2, 5, 8, 1, 4].some(isBiggerThan10);  // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
还可以使用箭头函数实现相同的效果。

[2, 5, 8, 1, 4].some(x => x > 10);  // false
[12, 5, 8, 1, 4].some(x => x > 10); // true

2.实现

mySome(callback, thisValue) {
  const arr = thisValue ? thisValue.data : this.data;
  if (arr.length === 0) return false;

  for (let i = 0; i < arr.length; i++) {
    if (callback(arr[i], i, arr)) {
      return true;
    }
  }
  return false;
}

.includes

1.基本使用

定义:用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false。

返回值:boolean

参数:valueToFind,fromIndex(可选)

  • 第一个参数是‘需要查找的元素值’,
  • 第二个参数是‘从哪个索引处开始查找’,
  • 第二个参数如果为负数,则会按升序从 array.length + fromIndex 的索引开始查找(负数就是从倒数第几个数开始找)。不会改变查找顺序
  • 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0。
var a = [1,2,3,4,5,6]
a.includes(2)  // true
a.includes(2,3)  // false
a.includes(5,-2)  // true
a.includes(5,-1)  // false

2.实现

myIncludes(elem, startIndex) {
  const arr = this.data;
  const arrLength = arr.length;
  let startFindIndex = startIndex < 0 ? startIndex + arrLength : startIndex;
  if (startFindIndex > arrLength) return false;
  if (startFindIndex < 0) startFindIndex = 0;
  for (let i = startFindIndex; i < arrLength; i++) {
    if (arr[i] === elem) return true;
  }
  return false;
}

完整代码

const CollectArray = require("../01集合类/index");

class FindArray extends CollectArray {
  myFindIndex(callback, thisValue) {
    const arr = thisValue ? thisValue.data : this.data;
    if (arr.length === 0) return -1;
    for (let i = 0; i < arr.length; i++) {
      if (callback(arr[i], i, arr)) {
        return i;
      }
    }
    return -1;
  }

  myFind(callback, thisValue) {
    const arr = thisValue ? thisValue.data : this.data;
    if (arr.length === 0) return;
    for (let i = 0; i < arr.length; i++) {
      if (callback(arr[i], i, arr)) {
        return arr[i];
      }
    }
    return undefined;
  }

  myIndexOf(elem, startIndex) {
    const arr = this.data;
    const arrLength = arr.length;
    let startFindIndex = startIndex < 0 ? arrLength + startIndex : startIndex;
    if (startFindIndex > arrLength || arrLength === 0) return -1;
    if (startFindIndex < -arrLength) startFindIndex = 0;
    for (let i = startFindIndex; i < arrLength; i++) {
      if (arr[i] === elem) {
        return i;
      }
    }
    return -1;
  }

  myLastIndexOf(elem, startIndex) {
    const arr = this.data;
    const arrLength = arr.length;
    let startFindIndex = startIndex < 0 ? arrLength + startIndex : startIndex;
    if (startFindIndex > arrLength) startFindIndex = arrLength;
    if (startFindIndex < -arrLength) return -1;
    for (let i = startFindIndex; i >= 0; i--) {
      if (arr[i] === elem) return i;
    }
    return -1;
  }

  myEvery(callback, thisValue) {
    const arr = thisValue ? thisValue.data : this.data;
    if (arr.length === 0) return false;

    for (let i = 0; i < arr.length; i++) {
      if (!callback(arr[i], i, arr)) {
        return false;
      }
    }
    return true;
  }

  mySome(callback, thisValue) {
    const arr = thisValue ? thisValue.data : this.data;
    if (arr.length === 0) return false;

    for (let i = 0; i < arr.length; i++) {
      if (callback(arr[i], i, arr)) {
        return true;
      }
    }
    return false;
  }

  myIncludes(elem, startIndex) {
    const arr = this.data;
    const arrLength = arr.length;
    let startFindIndex = startIndex < 0 ? startIndex + arrLength : startIndex;
    if (startFindIndex > arrLength) return false;
    if (startFindIndex < 0) startFindIndex = 0;
    for (let i = startFindIndex; i < arrLength; i++) {
      if (arr[i] === elem) return true;
    }
    return false;
  }
}

const findArr = new FindArray();
findArr.initArr([2, 2, 3, 4, 5, 5]);

findArr.logHelper("findIndex", findArr.getArray(), () => {
  return findArr.myFindIndex((item, index, arr) => {
    return item === 3;
  });
});

findArr.logHelper("find", findArr.getArray(), () => {
  return findArr.myFind((item, index, arr) => {
    return item === 5;
  });
});

findArr.logHelper("indexOf", findArr.getArray(), () => {
  return findArr.myIndexOf(2, -5);
});

findArr.logHelper("lastIndexOf", findArr.getArray(), () => {
  return findArr.myLastIndexOf(2, -5);
});

findArr.logHelper("every", findArr.getArray(), () => {
  return findArr.myEvery((item, index, arr) => {
    return item > 2;
  });
});

findArr.logHelper("some", findArr.getArray(), () => {
  return findArr.mySome((item, index, arr) => {
    return item > 10;
  });
});

findArr.logHelper("includes", findArr.getArray(), () => {
  return findArr.myIncludes(2, 6);
});

3.拼接、附加、翻转数组

.concat

1.基本使用

  • 定义:用于将一个或多个数组或值合并至一个数组中,该方法不会改变原数组
  • 返回值:新数组
  • 参数:值或者数组(可以传对象)
  • 注意concat返回的是一个浅拷贝
//连接两个数组
const arr = [1, 2, 3].concat([4, 5]) 
console.log(arr) // [1,2,3,4,5]

//连接三个数组
const arr1 = [1, 2]
const arr2 = [3, 4]
const arr3 = [5, 6]
const arr4 = arr1.concat(arr2, arr3)
console.log(arr4) // [1, 2, 3, 4, 5, 6]

//连接值和数组
const arr1 = [1, 2]
const arr2 = 3
const arr3 = [5, 6]
const arr4 = arr1.concat(arr2, arr3)
console.log(arr4) // [1, 2, 3, 5, 6]

//连接对象
const arr1 = [1]
const arr2 = [3, 4]
const arr3 = {
  a: 1,
  b: 2
}
const arr4 = arr1.concat(arr2, arr3)
console.log(arr4) // [1, 3, 4, {a:1, b:2}]

2.实现

myConcat(...array) {
  let arr = this.data;
  let result = [...arr];
  for (let i = 0; i < array.length; i++) {
    if (Array.isArray(array[i])) {
      result = [...result, ...array[i]];
    } else {
      result.push(array[i]);
    }
  }
  return result;
}

.join

1.基本使用

语法array.join(separator)

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

返回值:string 该字符串是通过把 array 的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 separator 字符串而生成的。

参数:分隔符,默认为,

var arr = ["a","b","c"];
array = arr.join(undefined);
console.log(array);//a,b,c 
array = arr.join("|");
console.log(array);//a|b|c

2.实现

myJoin(seperate) {
  let seperateStr = seperate === undefined ? "," : seperate;
  let arr = this.data;
  const result = arr.reduce((acc, item, index) => {
    if (index === 0) {
      return `${item}`;
    } else {
      return `${acc}${seperateStr}${item}`;
    }
  });
  return result;
}

.reverse

1.基本使用

  • 语法array.join(separator)
  • 定义:数组反转
  • 返回值:颠倒顺序的数组
  • 参数:无
const list = [1, 2, 3, 4, 5];
list.reverse();  ``// [5, 4, 3, 2, 1]

2.实现

myReverse() {
  let array = this.data;
  let mid = Math.floor(this.data.length / 2);
  const arrayLength = array.length - 1;
  let temp;
  for (let i = 0; i <= mid; i++) {
    temp = array[i];
    array[i] = array[arrayLength - i];
    array[arrayLength - i] = temp;
  }
  return array;
}

完整代码

const FindArray = require("../02检索类/index");

class OpArray extends FindArray {
  myConcat(...array) {
    let arr = this.data;
    let result = [...arr];
    for (let i = 0; i < array.length; i++) {
      if (Array.isArray(array[i])) {
        result = [...result, ...array[i]];
      } else {
        result.push(array[i]);
      }
    }
    return result;
  }

  myJoin(seperate) {
    let seperateStr = seperate === undefined ? "," : seperate;
    let arr = this.data;
    const result = arr.reduce((acc, item, index) => {
      if (index === 0) {
        return `${item}`;
      } else {
        return `${acc}${seperateStr}${item}`;
      }
    });
    return result;
  }

  myReverse() {
    let array = this.data;
    let mid = Math.floor(this.data.length / 2);
    const arrayLength = array.length - 1;
    let temp;
    for (let i = 0; i <= mid; i++) {
      temp = array[i];
      array[i] = array[arrayLength - i];
      array[arrayLength - i] = temp;
    }
    return array;
  }
}

// const findArr = new OpArray();
// findArr.initArr([1, 2, 3, 4, 5]);

// findArr.logHelper("concat", findArr.getArray(), () => {
//   return findArr.myConcat(6, [7, 8]);
// });

// findArr.logHelper("concat", findArr.getArray(), () => {
//   return findArr.myJoin("$");
// });

// findArr.logHelper("reverse", findArr.getArray(), () => {
//   return findArr.myReverse();
// });

module.exports = OpArray;

4.添加、删除、追加值

.shift

1.基本使用

  • 定义:方法用于删除并返回数组的开头一个元素。
  • 返回值:删除的元素
  • 参数:无
const colors = ['green', 'red']; 
const item = colors.shift(); 
console.log(item); // 'green' 
console.log(colors); // ['red']

2.实现

myShift() {
  const arr = this.data;
  const result = arr[0];
  for (let i = 1; i < arr.length; i++) {
    arr[i - 1] = arr[i];
  }
  arr.length -= 1;
  return result;
}

unShift

1.基本使用

  • 定义:往数组前面新增元素
  • 返回值:新数组长度
  • 参数:一个或多个元素
const colors = ['red'];
colors.unshift('blue', 'grey');
console.log(colors); // ['blue', 'grey', 'red']

2.实现

myUnShift(...value) {
  const arr = this.data;
  const mergeArr = [...value, ...arr];
  for (let i = 0; i < mergeArr.length; i++) {
    arr[i] = mergeArr[i];
  }
  return arr.length;
}

Array.slice()切片

1.基本使用

  • 语法Array.slice(start,end)
  • 定义:通过索引位置获取新的数组,该方法不会修改原数组,只是返回一个新的子数组。
  • 返回值:新数组,不包括终止索引。
  • 参数:
    • Array - 原始数组;
    • start - 必填;设定新数组的起始位置;如果是负数,则表示从数组尾部开始算起(-1指最后一个元素,-2 指倒数第二个元素,以此类推)。
    • end - 可选;设定新数组的结束位置;如果不填写该参数,默认到数组结尾;如果是负数,则表示从数组尾部开始算起(-1 指最后一个元素,-2指倒数第二个元素,以此类推)。
//获取仅包含最后一个元素的子数组
var arr=[1,2,3,4,5];
arr.slice(-1);//[5]

//获取不包含最后一个元素的子数组
var arr=[1,2,3,4,5];
arr.slice(0, -1);//[1,2,3,4]

//从第二个元素开始的所有元素的子数组
var arr=[1,2,3,4,5];
arr.slice(1);//[2,3,4,5]

2.实现

mySlice(start, end) {
  const arr = this.data;
  const arrLength = arr.length;
  let startIndex = start < 0 ? arrLength + start : start;
  if (startIndex < -arrLength) return (startIndex = 0);
  if (startIndex > arrLength) return [];
  let endIndex = end < 0 ? arrLength + end : end;
  if (endIndex < 0) return [];
  const result = [];
  for (let i = startIndex; i < endIndex; i++) {
    result.push(arr[i]);
  }
  return result;
}

splice

1.基本使用

  • 定义:往数组前面新增、插入、删除元素
  • 返回值:无
  • 参数:
    • splice(position, count) 表示从 position 索引的位置开始,删除count个元素
    • splice(position, 0, a, b, ...) 表示从 position 索引的元素后面插入一系列的元素
    • splice(postion, count, a, b, ...) 表示从 position 索引的位置开始,删除 count 个元素,然后再插入一系列的元素
var arr = [1, 2, 3, 4, 5];
arr.splice(5, 0, 6);
console.log(arr); // [1, 2, 3, 4, 5, 6];

2.实现

mySplice(start, removeNum, ...addValues) {
  const arr = this.data;
  // 第一段: 无需处理
  const firstPart = arr.slice(0, start);
  // 第二段: 删除的部分
  const delPart = arr.slice(start, start + removeNum);
  console.log("✅ ~ delPart:", delPart);
  // 第三段: 剩余的部分
  const surplusPart = arr.slice(start + removeNum);
  // 增加的部分
  const addPart = [...addValues];
  const result = [...firstPart, ...addPart, ...surplusPart];
  return result;
}

pop

1.基本使用

  • 定义:方法用于删除并返回数组的最后一个元素。
  • 返回值:删除的元素
  • 参数:无
const color = ['green','red']
const item = colors.pop(); 
console.log(item); // 'red' 
console.log(colors); // ['green']

2.实现

myPop() {
  const arr = this.data;
  const delElem = arr[arr.length - 1];
  arr.length -= 1;
  return delElem;
}

.push

1.基本使用

  • 定义:往数组后面新增元素
  • 返回值:无
  • 参数:一个或多个元素
const colors = []; 
colors.push('green', 'red'); 
console.log(colors); // ['green', 'red']

2.实现

myPush(values) {
  const arr = this.data;
  const arrLength = arr.length;
  const addArrLength = values.length;
  arr = [...arr, ...values];
  arr.length = arrLength + addArrLength;
}

.fill

1.基本使用

语法array.fill(value, start, end)

定义:方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。

返回值:修改后的数组

参数value | 必需。填充的值。 start | 可选。开始填充位置。 end | 可选。停止填充位置 (默认为 array.length)

[1, 2, 3].fill(4); // [4, 4, 4]
[1, 2, 3].fill(4, 1); // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3); // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2); // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN); // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5); // [1, 2, 3]
Array(3).fill(4); // [4, 4, 4] 创建长度为几,值都相同的数组
[].fill.call({ length: 3 }, 4); // {0: 4, 1: 4, 2: 4, length: 3}

2.实现

myFill(fillNum, start, end) {
  const arr = this.data;
  let startIndex = start < 0 ? start + arr.length : start;
  if (startIndex > arr.length) return arr;
  let endIndex = end < 0 ? end + arr.length : end;
  if (endIndex < 0) return arr;
  for (let i = startIndex; i < endIndex; i++) {
    arr[i] = fillNum;
  }
  return arr;
}

四、数组扁平化

1.flat方法

参数为深度,默认值为1

let a = [1, [2, 3, [4, 5]]];
a.flat();
console.log("✅ ~ a.flat():", a.flat());
console.log("✅ ~ a.flat():", a.flat(Infinity));

2.for循环(递归)

递归的思路,保证递归终止条件即可,将结果返回

myFlatFor() {
  const arr = this.data;
  const flatFunc = (array) => {
    let result = [];
    for (let i = 0; i < array.length; i++) {
      if (Array.isArray(array[i])) {
        result = [...result, ...flatFunc(array[i])];
      } else {
        result.push(array[i]);
      }
    }
    return result;
  };
  return flatFunc(arr);
}

3.while+some+concat方法

const arr1 = [1, 2, [3], [1, 2, 3, [4, [2, 3, 4]]]];
const arr2 = [1, 2, [3]];
function flatten(arr) {
  while (arr.some((item) => Array.isArray(item))) {
    // 多个值连起来,如果是值,直接放进去,数组,拆开
    arr = [].concat(...arr);
    console.log("✅ ~ arr:", arr);
    //arr = Array.prototype.concat.apply([],arr);
  }
  return arr;
}
flatten(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
console.log("✅ ~ flatten(arr1):", flatten(arr1));

3.1 解释[].concat(...arr);

展平数组一层的能力来源于...(扩展运算符)的工作原理。在JavaScript中,扩展运算符可以在函数调用、数组字面量或对象字面量中使用,其作用是将一个可迭代对象(例如数组)“展开”为其元素。当用于数组时,扩展运算符会将数组中的每个元素作为单独的参数传递(或展开到一个新的数组中),从而能够实现数组的一层展平。

假设存在一个数组 arr = [1, [2, 3], 4],当使用扩展运算符 ...arr,其效果相当于写出 1, [2, 3], 4,即将数组 arr 中的每个元素分别拿出来。如果把这个操作的结果放到一个新数组中,就像这样 [...arr],结果仍然是 [1, [2, 3], 4],因为扩展运算符仅仅是分别取出了数组 arr 中的每个元素,并没有进一步展平嵌套的数组 [2, 3]

然而,当结合使用扩展运算符和concat方法时,可以实现嵌套数组的一层展平。具体来说,在扩展运算符将数组元素分别展开之后,concat方法将这些元素合并到一个新的数组中。如果在这个过程中碰到了数组元素,这个数组元素将作为整体被合并进去,而不是它的子元素。但是,因为在这个步骤之前它已经被展开了,所以实际上就达到了展平一层的效果。

例如:

let arr = [1, [2, 3], 4];
let flatArr = [].concat(...arr);

在这里,...arrarr展开为1, [2, 3], 4,然后concat将这些元素合并到一个新数组中。因为[2, 3]在这个过程中作为一个元素被处理(尽管它自己是一个数组),所以最终结果是[1, 2, 3, 4],实现了一层展平。

简而言之,扩展运算符本身并不直接展平数组,但当与如concat方法这样的操作结合时,扩展运算符通过分解数组元素,使得嵌套数组能够被视为顶层的单独元素处理,从而实现展平一层的效果。

3.2 解释concat.apply([],arr)

apply 方法

apply是JavaScript中函数对象的一个方法。它允许你调用一个函数,同时允许你为调用设置this值,以及以数组形式传递参数。

apply接收两个参数:

  1. thisArg:函数运行时使用的this值。
  2. argsArray:一个数组或类数组对象,其中的数组元素将作为单独的参数传递给被调用的函数。

将apply方法与concat结合使用

Array.prototype.concat.apply([], arr)被调用时,这段代码实际上是在使用apply方法以一种特殊的方式调用concat方法。

在这里,concat方法的this值被设置为一个空数组[]arr参数是一个包含嵌套数组的数组,被作为第二个参数传给apply,表示应当作为参数列表传递给concat方法。

功能解释

假设arr = [1, [2, 3], 4];,调用Array.prototype.concat.apply([], arr);时,实际上是把arr的元素分解为独立参数传给了concat方法:

  • 这相当于[].concat(1, [2, 3], 4);

由于concat能接受多个参数,并将它们合并入一个新数组,上述调用结果会是[1, 2, 3, 4]

因此,Array.prototype.concat.apply([], arr);正通过这种方式实现了数组的一层展平:它将一个多层或嵌套的数组arr中的每个元素(不管是单独的值还是子数组)作为独立的参数传入concat方法,从而只合并第一层的元素或数组,得到一个新的一层展平后的数组。

4.reduce递归

跟递归的思路是一致的,只是不需要处理返回,reduce自己返回了

myFlatFor() {
    const arr = this.data;
    const flatFunc = (array) => {
      let result = [];
      for (let i = 0; i < array.length; i++) {
        if (Array.isArray(array[i])) {
          result = [...result, ...flatFunc(array[i])];
        } else {
          result.push(array[i]);
        }
      }
      return result;
    };
    return flatFunc(arr);
  }

5.stack反嵌套

如果是数组,则展开一层,放到栈顶,每次从栈顶获取判断

 myFlatStack() {
    const arr = this.data;
    const whileStack = (array) => {
      const stack = [...array];
      const result = [];
      while (stack.length) {
        // 如果是数组,则展开一层,放到栈顶,每次从栈顶获取判断
        const stackTop = stack.shift();
        if (Array.isArray(stackTop)) {
          stack.unshift(...stackTop);
        } else {
          result.push(stackTop);
        }
      }
      return result;
    };
    return whileStack(arr);
 }

五、数组去重

1.双重循环

两个数组,arr,res,遍历arr,再判断arr是否在res中

myForDelRepeat() {
    const arr = [...this.data];
    const res = [arr[0]];

    for (let i = 1; i < arr.length; i++) {
      let flag = true;
      for (let j = 0; j < res.length; j++) {
        if (arr[i] === res[j]) {
          flag = false;
          break;
        }
      }
      if (flag) res.push(arr[i]);
    }
    return res;
}

2.indexOf

方法一:也是判断arr[i]是否在res中

myIndexOfDelRepeat() {
    const arr = [...this.data];
    const res = [arr[0]];
    for (let i = 0; i < arr.length; i++) {
      if (res.indexOf(arr[i]) === -1) {
        res.push(arr[i]);
      }
    }
    return res;
}

方法二:根据indexOf的下标是否一致进行过滤

myIndexOfFilterDelRepeat() {
  const arr = [...this.data];
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  }, []);
}

3.相邻元素对比

先将数组排序,然后再相邻元素进行对比,不相同的push

mySortAdjacentDelRepeat() {
  let arr = [...this.data];
  const res = [];
  arr = arr.sort();
  for (let i = 0; i < arr.length; i++) {
    // 跟前一个,后一个对比都是一样的
    if (arr[i] !== arr[i + 1]) {
      res.push(arr[i]);
    }
  }
  return res;
}

4.利用对象属性

这个效率最好,还可以记录每个数出现的数量

myUseObjDelRepeat() {
  let arr = [...this.data];
  const res = [];
  const obj = {};
  for (let i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      res.push(arr[i]);
      obj[arr[i]] = 1;
    } else {
      obj[arr[i]]++;
    }
  }
  console.log("✅ ~ obj:", obj);
  return res;
}

5.set的特性

set中数据不重复。Set函数可以接受一个数组(或类数组对象)作为参数来初始化,利用该特性也能做到给数组去重

myNewSetDelRepeat() {
  const arr = [...this.data];
  return [...new Set(arr)];
}

myArrayFromNewSetDelRepeat() {
  const arr = [...this.data];
  return Array.from(new Set(arr));
}

6.完整代码

const FlatArray = require("../05数组扁平化/index");
class NonRepeatArray extends FlatArray {
  myForDelRepeat() {
    const arr = [...this.data];
    const res = [arr[0]];

    for (let i = 1; i < arr.length; i++) {
      let flag = true;
      for (let j = 0; j < res.length; j++) {
        if (arr[i] === res[j]) {
          flag = false;
          break;
        }
      }
      if (flag) res.push(arr[i]);
    }
    return res;
  }

  myIndexOfDelRepeat() {
    const arr = [...this.data];
    const res = [arr[0]];
    for (let i = 0; i < arr.length; i++) {
      if (res.indexOf(arr[i]) === -1) {
        res.push(arr[i]);
      }
    }
    return res;
  }

  myIndexOfFilterDelRepeat() {
    const arr = [...this.data];
    return arr.filter((item, index) => {
      return arr.indexOf(item) === index;
    }, []);
  }

  mySortAdjacentDelRepeat() {
    let arr = [...this.data];
    const res = [];
    arr = arr.sort();
    for (let i = 0; i < arr.length; i++) {
      // 跟前一个,后一个对比都是一样的
      if (arr[i] !== arr[i + 1]) {
        res.push(arr[i]);
      }
    }
    return res;
  }

  myUseObjDelRepeat() {
    let arr = [...this.data];
    const res = [];
    const obj = {};
    for (let i = 0; i < arr.length; i++) {
      if (!obj[arr[i]]) {
        res.push(arr[i]);
        obj[arr[i]] = 1;
      } else {
        obj[arr[i]]++;
      }
    }
    console.log("✅ ~ obj:", obj);
    return res;
  }

  myNewSetDelRepeat() {
    const arr = [...this.data];
    return [...new Set(arr)];
  }

  myArrayFromNewSetDelRepeat() {
    const arr = [...this.data];
    return Array.from(new Set(arr));
  }
}

const repeatArr = new NonRepeatArray();
repeatArr.initArr([2, 2, 3, 3, 4, 5, 5, 6]);

repeatArr.logHelper("myForDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.myForDelRepeat();
});

repeatArr.logHelper("myIndexOfDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.myIndexOfDelRepeat();
});

repeatArr.logHelper("myIndexOfFilterDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.myIndexOfFilterDelRepeat();
});

repeatArr.logHelper("mySortAdjacentDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.mySortAdjacentDelRepeat();
});

repeatArr.logHelper("myUseObjDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.myUseObjDelRepeat();
});

repeatArr.logHelper("myNewSetDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.myNewSetDelRepeat();
});

repeatArr.logHelper("myArrayFromNewSetDelRepeat", repeatArr.getArray(), () => {
  return repeatArr.myArrayFromNewSetDelRepeat();
});

结语

手写代码仓库github.com/zhuling904/…

image.png

本篇文章到此就结束了,欢迎在评论区交流。

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论,  支持一下博主~