在日常开发中,我们经常使用数组的 API。本文将详细介绍一些常用的数组操作方法,并提供其实现代码。

unsetunset任意位置插入单个成员unsetunset

数组任意位置插入单个成员,这个 api 几乎可以作为数组插入的一个最基本的方法来处理,实现如下

按照数组 api 的规则,我们为数组增加了成员,那么要返回数组的长度

function insert(arr, idx, item{
  // 循环为什么要倒着写?看下面解释
  for (let i = arr.length - 1; i > idx - 1; i--) {
    arr[i + 1] = arr[i];
  }
  arr[idx] = item;
  return arr.length;
}

数组初始二维化方法_二维数组初始化_数组初始二维化什么意思

循环为什么要倒着写?unsetunset任意位置移除单个成员unsetunset

移除元素跟插入元素应该是一组对应的 api,同理要返回被删除的元素

删除的核心思想就是:从删除项开始,数组所有成员左移一位,最后长度减一即可

function remove(arr, idx{
  const r = arr[idx];
  for (let i = idx; i < arr.length; i++) {
    arr[i] = arr[i + 1];
  }
  arr.length--;
  return r;
}

unsetunset转字符串 joinunsetunset

转字符其实就是做一个字符串拼接

function join(arr, symbol = ''{
  // 核心思想是拼接字符串
  let r = arr[0];
  for (let i = 1, len = arr.length; i < len; i++) {
    r += symbol + arr[i];
  }
  return r;
}

unsetunset数组截取 sliceunsetunset

slice 截取数组,不改变原数组,返回一个新的数组,是一个浅复制,要创建一个新的数组,所以就可以用到前面写的 insert 了

function slice(arr, start = 0, end = arr.length{
  end = end > arr.length ? arr.length : end;
  const r = [];
  for (let i = start; i < end; i++) {
    insert(r, r.length, arr[i]);
  }
  return r;
}

unsetunset栈、队列操作unsetunset

JavaScript 中的数组可以很好的模拟栈和队列的数据操作

push

push 向数组末尾添加一个或多个元素,并返回新数组的长度

function push(arr, ...item{
  const len = arr.length;
  // const args = [].slice.call(arguments, 1)
  for (let i = 0; i < item.length; i++) {
    arr[len + i] = item[i];
  }
  return arr.length;
}

pop

pop 删除数组最后一个元素,并返回该元素

function pop(arr{
  const len = arr.length;
  if (!len) return void 0;
  const r = arr[len - 1];
  arr.length--;
  return r;
}

unshift

unshift 在数组头部添加一个或多个元素,并返回新数组的长度

function unshift(arr, item{
  for (let i = arr.length; i > 0; i--) {
    arr[i] = arr[i - 1];
  }
  arr[0] = item;
  return arr.length;
}

unshift 可以像 push 那样传递多个参数,所以要考虑这个情况,要保证数据插入的正确性,具体实现如下

function unshift(arr{
  const args = [].slice.call(arguments1);
  const argsLen = args.length;
  const len = arr.length + argsLen;
  for (let i = len - 1; i > argsLen - 1; i--) {
    arr[i] = arr[i - argsLen];
  }
  // for (let i = arr.length - 1; i >= 0; i--) {
  //   arr[i + args.length] = arr[i];
  // }
  for (let i = 0; i < args.length; i++) {
    arr[i] = args[i];
  }
  return arr.length;
}

shift

shift 删除数组的第一个元素,并返回该元素

function shift(arr{
  const r = arr[0];
  for (let i = 1; i < arr.length; i++) {
    arr[i - 1] = arr[i];
  }
  arr.length--;
  return r;
}

unsetunset数组反转unsetunset

数组反转 reverse 也是一个会让原数组发生改变的 api,返回改变后的数组

function reverse(arr{
  const len = arr.length;
  const lenHalf = len / 2;
  for (let i = 0; i < lenHalf; i++) {
    const temp = arr[i];
    arr[i] = arr[len - 1 - i];
    arr[len - i - 1] = temp;
  }
  return arr;
}

unsetunset遍历数组unsetunset

数组遍历是很常用的 api,有直接遍历数组的,有对数组进行处理返回对应结果的,有筛选数据的,不会改变原数组(对引用类型的数组成员进行修改还是会改变的),属于纯函数常用的 forEach、map、filter、find、some、every、reduce 等

forEach

遍历数组中的每个元素,执行提供的回调函数

function forEach(arr, cb, ctx = null{
  for (let i = 0; i < arr.length; i++) {
    cb.call(ctx, arr[i], i, arr);
  }
}

在 forEach 中用 return 是不会返回任何结果的,函数还会继续执行

中断方法:

接下来我们看看 some 和 every 的实现

some

some 函数是一个很好用的函数,判断数组成员有任意一项满足条件则返回 true

function some(arr, cb, ctx = null{
  for (let i = 0; i < arr.length; i++) {
    if (cb.call(ctx, arr[i], i, arr)) {
      return true;
    }
  }
  return false;
}

every

every 与 some 正好相反,数组成员都满足条件返回 true,否则返回 false

function every(arr, cb, ctx = null{
  for (let i = 0; i < arr.length; i++) {
    if (!cb.call(ctx, arr[i], i, arr)) {
      return false;
    }
  }
  return true;
}

map

map 创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。如果使用中没有返回结果则与 forEach 的效果一致

function map(arr, cb, ctx = null{
  const r = [];
  for (let i = 0; i < arr.length; i++) {
    r.push(cb.call(ctx, arr[i], i, arr));
  }
  return r;
}

filter

filter 会对返回一组满足条件的数组成员,所以接受的函数中需要返回一个布尔值

function filter(arr, cb, ctx = null{
  const r = [];
  for (let i = 0; i < arr.length; i++) {
    const ret = cb.call(ctx, arr[i], i, arr);
    if (ret) {
      r.push(arr[i]);
    }
  }
  return r;
}

find

find 查找数组中满足条件的第一个成员

function find(arr, cb, ctx = null{
  for (let i = 0; i < arr.length; i++) {
    if (cb.call(ctx, arr[i], i, arr)) {
      return arr[i];
    }
  }
  return void 0;
}

findIndex 跟 find 一样,只不过返回结果一个是返回数组成员,一个是数组下标

function findIndex(arr, cb, ctx = null{
  for (let i = 0; i < arr.length; i++) {
    if (cb.call(ctx, arr[i], i, arr)) {
      return i;
    }
  }
  return -1;
}

reduce

reduce 有个初始值的概念,初始值定了,返回结果就是在初始值上进行处理,返回每次对数组成员计算后的结果

function reduce(arr, cb, init, ctx = null{
  let r = init;
  for (let i = 0; i < arr.length; i++) {
    r = cb.call(ctx, r, arr[i], i, arr);
  }
  return r;
}

unsetunset合并数组 concatunsetunset

concat 合并一个数组,返回一个新的数组,也不会对原数发生改变,需要注意的是,参数可以是多个,可以是数组也可以是单个成员

function concat(arr, ...target{
  // const target = [].slice.call(arguments, 1)
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    insert(result, result.length, arr[i]);
  }
  for (let i = 0; i < target.length; i++) {
    const item = target[i];
    if (Array.isArray(item)) {
      for (let j = 0; j < item.length; j++) {
        insert(result, result.length, item[j]);
      }
    } else {
      insert(result, result.length, item);
    }
  }
  return result;
}

unsetunset扁平化数组 flatunsetunset

如下:一个多维数组,要求把数组扁平化成一个一维数组

const arr = [12, [214588], 34, [56, [78, [911]]]];
// 结果:[ 1, 2, 21, 45, 88, 3, 4, 5, 6, 7, 8, 9, 11 ]

function flatUseRegExp(arr{
  const str = JSON.stringify(arr).replace(/[|]/g'');
  return str.split(',').map(i => +i);
}

function flatUseToString(arr{
  return arr
          .toString()
          .split(',')
          .map(i => +i);
}

function flat(arr{
  let r = [];
  arr.forEach(item => {
    if (Array.isArray(item)) {
      r = r.concat(flat(item));
    } else {
      r.push(item);
    }
  });
  return r;
}

function flat(arr{
  while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}

unsetunset数组 at 方法unsetunset

根据索引值获取数组中的元素,支持正向和反向索引

function at(arr, n{
  n = Math.trunc(n) || 0;
  if (n < 0) n += arr.length;
  if (n < 0 || n >= arr.length) return undefined;
  return arr[n];
}

unsetunset将一维数组按指定长度拆分为二维数组:unsetunset

之前面试遇到一道题,有一个一维数组,我想要写个方法,方法接收两个参数,该数组和一个数字,然后得到一个根据这个数字而拆分成的多维数组,比如说我传递一个 3,那就数组中的成员就每三个成员组成一个新的数组

const arr = [1234567890];
// 结果:[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [ 0 ] ]

const addAxis = (arr, offset) => {
  const len = arr.length;
  // 偏移量计算如果正好能被整除那么就取传入的偏移量,否则就向下取整后加1
  const offsetNum = len % offset === 0 ? offset : ~~(len / offset + 1);
  const result = [];
  for (let i = 0; i < offsetNum; i++) {
    result.push(arr.slice(i * offset, i * offset + offset));
  }
  return result;
};

unsetunset数组原型上的实现unsetunset

根据上面的思路,这里直接给数组原型实现一波常用 api,代码较长,比较完整,细细评阅

function insert(arr, index, item{
  for (let i = arr.length - 1; i > index - 1; i--) {
    arr[i + 1] = arr[i];
  }
  arr[index] = item;
  return arr.length;
}

Array.prototype.insert = function(index, item{
  return insert(this, index, item);
};

Array.prototype.remove = function(index{
  const removeItem = this[index];
  for (let i = index; i < this.length; i++) {
    this[i] = this[i + 1];
  }
  this.length--;
  return removeItem;
};

Array.prototype.join2 = function(symbol = ','{
  let str = this[0] || '';
  for (let i = 1; i < this.length; i++) {
    str += symbol + this[i];
  }
  return str;
};

Array.prototype.slice2 = function(start = 0, end = this.length{
  end = end > this.length ? end : this.length;
  let r = [];
  for (let i = start; i < end; i++) {
    insert(r, r.length, this[i]);
  }
  return r;
};

Array.prototype.push2 = function(...args{
  const len = this.length;
  for (let i = 0; i < args.length; i++) {
    insert(this, len + i, args[i]);
  }
  return this.length;
};

Array.prototype.pop2 = function({
  const len = this.length;
  if (!len) return void 0;
  const popValue = this[len - 1];
  this.length--;
  return popValue;
};

Array.prototype.unshift2 = function({
  const argsLen = arguments.length;
  const len = this.length + argsLen;
  for (let i = len - 1; i > argsLen - 1; i--) {
    this[i] = this[i - argsLen];
  }
  for (let i = 0; i < argsLen; i++) {
    this[i] = arguments[i];
  }
  return this.length;
};

Array.prototype.shift2 = function({
  const r = this[0];
  for (let i = 1; i < this.length; i++) {
    this[i - 1] = this[i];
  }
  this.length--;
  return r;
};

Array.prototype.reverse2 = function({
  const len = this.length;
  for (let i = 0; i < len / 2; i++) {
    const temp = this[i];
    this[i] = this[len - i - 1];
    this[len - i - 1] = temp;
  }
  return this;
};

Array.prototype.forEach2 = function(callback, ctx = null{
  for (let i = 0; i < this.length; i++) {
    callback.call(ctx, this[i], i, this);
  }
};

Array.prototype.map2 = function(callback, ctx = null{
  const r = [];
  for (let i = 0; i < this.length; i++) {
    r.push(callback.call(ctx, this[i], i, this));
  }
  return r;
};

Array.prototype.filter2 = function(callback, ctx = null{
  const r = [];
  for (let i = 0; i < this.length; i++) {
    if (callback.call(ctx, this[i], i, this)) {
      r.push(this[i]);
    }
  }
  return r;
};

Array.prototype.some2 = function(callback, ctx = null{
  for (let i = 0; i < this.length; i++) {
    if (callback.call(ctx, this[i], i, this)) {
      return true;
    }
  }
  return false;
};

Array.prototype.every2 = function(callback, ctx = null{
  for (let i = 0; i < this.length; i++) {
    if (!callback.call(ctx, this[i], i, this)) {
      return false;
    }
  }
  return true;
};

Array.prototype.reduce2 = function(callback, init, ctx = null{
  // 初始值的初始化??
  let r = init;
  for (let i = 0; i < this.length; i++) {
    r = callback.call(ctx, r, this[i], i, this);
  }
  return r;
};

Array.prototype.find2 = function(callback, ctx = null{
  for (let i = 0; i < this.length; i++) {
    if (callback.call(ctx, this[i], i, this)) {
      return this[i];
    }
  }
};

Array.prototype.concat2 = function(...target{
  // 不改变原数组
  const r = [];
  for (let i = 0; i < this.length; i++) {
    insert(r, r.length, this[i]);
  }
  for (let i = 0; i < target.length; i++) {
    const item = target[i];
    if (Array.isArray(item)) {
      for (let j = 0; j < item.length; j++) {
        insert(r, r.length, item[j]);
      }
    } else {
      insert(r, r.length, item);
    }
  }
  return r;
};

Array.prototype.flat2 = function({
  // 不改变原数组
  let r = [];
  for (let i = 0; i < this.length; i++) {
    const item = this[i];
    if (Array.isArray(item)) {
      r = r.concat([].flat2.apply(item));
    } else {
      r.push(item);
    }
  }
  return r;
};

Array.prototype.at2 = function(n{
  n = Math.trunc(n) || 0;
  if (n < 0) n += this.length;
  if (n < 0 || n >= this.length) return undefined;
  return this[n];
};

const arr = [12345];

const len = arr.insert(3'你好');
console.log(len, arr);

const removeItem = arr.remove(3);
console.log(removeItem, arr);

console.log(arr.join2());
console.log(arr.slice2(24));
console.log(arr.push2('你好''不好'), arr, 'push');
console.log(arr.pop2(), arr, 'pop');
console.log(arr.pop2(), arr, 'pop');
console.log(arr.unshift2('unshift1''unshift2'), arr, 'unshift');
console.log(arr.shift2(), arr, 'shift');
console.log(arr.shift2(), arr, 'shift');
// console.log(arr.pop2(), arr, 'pop');

arr.forEach2(console.log);

console.log(arr.map2(x => x * 2), 'map');

console.log(arr.filter2(x => x > 3), 'filter');

console.log(arr.some2(x => x === 2), 'some');
console.log(arr.every2(x => x > 2), 'every');

console.log(arr.reduce2((x, y) => x + y, 0), 'reduce');
console.log(arr.find2(x => x === 1), 'find');

console.log(arr.concat2(678, [910]), 'concat');

const arr2 = [12, [34, [56]]];

console.log(arr2.flat2(), 'flat');

unsetunset结束语unsetunset

本文介绍了常用数组 API 的实现方法,包括插入和删除元素、字符串转换、截取、栈和队列操作、反转、遍历、过滤、查找、归并、合并、扁平化等操作。通过这些基础实现,能够深入理解 JavaScript 数组的工作机制,并在实际开发中灵活运用,提升编码效率和代码质量。希望这些示例和代码能够帮助你更好地掌握数组操作,写出更高效、更优雅的代码。


创业/副业必备:

本站已持续更新1W+创业副业顶尖课程,涵盖多个领域。

点击查看详情

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。