导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

数组的常用方法有哪些?

方法 作用 是否影响原数组
push 在数组后添加元素,返回长度
unshift 数组开头添加元素,返回长度
pop 删除数组最后一项,返回被删项
shift 删除数组第一项,返回被删项
reserve 反转数组,返回数组
sort 排序数组,返回数组
splice 截取数组,返回被截取部分
join 将数组变字符串,返回字符串
concat 连接数组
map 相同规则处理数组项,返回新数组
forEach 遍历数组
filter 过滤数组项,返回符合条件的数组
every 每一项符合规则才返回true
some 只要有一项符合规则就返回true
reduce 接受上一个return和数组下一项
flat 数组扁平化
slice 截取数组,返回被截取区间

⭐️ forEach 如何跳出循环?

function getItemById(arr, id) {
  var item = null;
  try {
    arr.forEach(function (curItem, i) {
      console.log(i)
      if (curItem.id == id) {
        item = curItem;
        throw Error();
      }
    })
  } catch (e) {}
  return item;
}

清空数组

var arr = [12, 1, 24, -123, 12123123];

arr.length = 0;
// arr.splice(0, arr.length);
// 通过将空数组 [] 赋值给数组(严格意义来说这只是将ary重新赋值为空数组,
// 之前的数组如果没有引用在指向它将等待垃圾回收。)
// arr = [];
console.log(arr);

如何实现数组的随机排序?

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 随机交换数组内的元素 ✅
function result(arr) {
    for (let i = 0; i < arr.length; i++) {
        // 随机索引【Math.random()返回一个浮点,  伪随机数在范围[0,1)】
        let randomIndex = parseInt(Math.random() * arr.length);
        // 存下当前正常索引值对应的数字
        let curNum = arr[i];
        // 将其重新赋值为随机索引对应的数字
        arr[i] = arr[randomIndex];
        // 将随机索引对应的数字替换为当前正常索引值对应的数字
        arr[randomIndex] = curNum;
    }
    return arr;
}

// sort()可以调用一个函数做为参数,如果这个函数返回的值为负数表示数组中的a项排在b项前
var arr = [1,2,3,4,5,6,7,8,9,10];
arr.sort(() => Math.random() - 0.5)
console.log(arr);

⭐ 对下面数组进行排重,并按升序排序,代码尽量简练:

const array = [ '2', 'b', '9','a','7','3','4','b','6', '4' ];
function handle(arr) {
   // ...your code
}
handle(array); // output: ['2', '3', '4', '6', '7', '9', 'a', 'b']
const array = [ '2', 'b', '9','a','7','3','4','b','6', '4' ];
function handle(arr) {
  return Array.from(new Set(arr))
          .sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0));
}
console.log(handle(array)); // output: ['2', '3', '4', '6', '7', '9', 'a', 'b']

⭐ 自定义 unshift

var arr = [1, 2, 3];

Array.prototype.myUnshift = function() {
  // 在数组打头,添加一个或多个元素,最后返回最新数组的长度
  var len = arguments.length;
  for (let i = len - 1; i >= 0; i--) {
    const element = arguments[i];
    this.splice(0, 0, element);
  }
  return this.length;
}

// console.log(arr.unshift(3, 2, 1), arr);
console.log(arr.myUnshift(3), arr);
var arr = [1, 2, 3];

Array.prototype.myUnshift = function() {
  var pos = 0;
  for (let i = 0; i < arguments.length; i++) {
    const element = arguments[i];
    this.splice(pos, 0, element);
    pos++;
  }
  
  return this.length;
}

// console.log(arr.unshift(3, 2, 1), arr);
console.log(arr.myUnshift(3, 2, 1), arr);

⭐ 数组按照元素的总字节数长度排序

var arr = ['Are you OK', 'Lance', 'Jerry', '我爱你'];
// unicode 0-255 1个字节;256 2个字节

var getBytes = function(str) {
	// 先把每个字符都当一个字节算
  var bytes = str.length;

  for (var i = 0; i < str.length; i++) {
  	if (str.charCodeAt(i) > 255) {
    	bytes++;
    }
  }

  return bytes;
}

arr.sort(function(a, b) {
	return getBytes(a) - getBytes(b);
});

// 结果: [ 'Lance', 'Jerry', '我爱你', 'Are you OK' ]

⭐ 数组去重

1. 循环 通过 hasOwnProperty 判断新数组是否有该属性(问题:{} 会被解析成'[object Object]')
var arr = [
  {}, {},
  '', '',
  233, 233, '233',
  'abc',
  undefined,
  null, null,
  NaN, NaN,
  123,
  [2], [2],
  [2, 3]
];

Array.prototype.myUnique = function() {
	var hash = {},
      newArr = [];
  for (var i = 0; i < this.length; i++) {
    var item = this[i];
  	if (!hash.hasOwnProperty(typeof item + JSON.stringify(item))) {
    	hash[typeof item + JSON.stringify(item)] = item;
      newArr.push(item);
    }
  }

  return newArr;
};

console.log(arr.myUnique());
// [
//   {},       '',
//   233,      '233',
//   'abc',    undefined,
//   null,     NaN,
//   123,      [ 2 ],
//   [ 2, 3 ]
// ]
2. ES6 Set
Array.prototype.myUnique = function() {
	return Array.from(new Set(this));
};

// [
//   {},        {},
//   '',        233,
//   '233',     'abc',
//   undefined, null,
//   NaN,       123,
//   [ 2 ],     [ 2 ],
//   [ 2, 3 ]
// ]
3. 利用includes检测数组是否有某个值  或者indexOf判断索引
Array.prototype.myUnique = function() {
	let arr = [];
  for (let i = 0; i < this.length; i++) {
    if (!arr.includes(this[i])) {
      arr.push(this[i]);
    }
  }
  return arr;
};

// [
//   {},        {},
//   '',        233,
//   '233',     'abc',
//   undefined, null,
//   NaN,       123,
//   [ 2 ],     [ 2 ],
//   [ 2, 3 ]
// ]
4. Map
Array.prototype.myUnique = function() {
  let map = new Map(), arr = [];
  for (let i = 0; i < this.length; i++) {
    if (!map.has(this[i])) {
      map.set(this[i], this[i]);
      arr.push(this[i]);
    }
  }
  return arr;
}

// [
//   {},        {},
//   '',        233,
//   '233',     'abc',
//   undefined, null,
//   NaN,       123,
//   [ 2 ],     [ 2 ],
//   [ 2, 3 ]
// ]
5. filter (问题:无法识别NaN)
Array.prototype.myUnique = function() {
  return this.filter((v, idx) => {
    return this.indexOf(v, 0) === idx;
  });
}

// [
//   {},        {},
//   '',        233,
//   '233',     'abc',
//   undefined, null,
//   123,       [ 2 ],
//   [ 2 ],     [ 2, 3 ]
// ]

找出正数组中的最大差值

var arr = [10,5,11,7,8,9]; // 11-5=6

function getMaxProfit(arr) {
  var min = max = arr[0];
  for(var i = 0; i < arr.length; i++) {
    min = min <= arr[i] ? min : arr[i];
    max = max >= arr[i] ? max : arr[i];
  }
  return Math.abs(max - min);
}

⭐ 从排序数组中删除重复项

// 若 nums = [1,1,2], 则函数应返回长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
// 若 nums = [0,0,1,1,1,2,2,3,3,4], 则返回 5, 并且原数组被修改为 0, 1, 2, 3, 4。
var nums = [0,0,1,1,1,2,2,3,3,4];
function removeDuplicates(arr) {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    if (arr[i] === arr[i + 1]) {
      arr.splice(i, 1);
      i--;
      len--;
    }
  }
  return arr;
}
console.log(removeDuplicates(nums));

找出数组中出现次数最多的元素,并给出其出现过的位置

var arr = [1, 1, 2, 1, 10, 10, 11, 1, 7];
function fn(arr) {
  let hash = {};
  arr.forEach((v, idx) => {
    if (!hash[v]) {
      hash[v] = {
        target: v,
        idxs: [idx]
      }
    } else {
      hash[v].idxs.push(idx);
    }
  });
  let count = 0, ret = null;
  Object.values(hash).forEach(v => {
    if (v.idxs.length > count) {
      ret = v;
      count = v.idxs.length;
    }
  });
  return ret;
}
console.log(fn(arr));

⭐ 将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组

已知如下数组:

var arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];

编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组

// ES6
// 扁平化数组
Array.prototype.flat = function() {
  return [].concat(...this.map(item => Array.isArray(item) ? item.flat() : [item]))
}
// 数组去重
Array.prototype.unique = function() {
  return [...new Set(this)]
}
// 数组排序
const sort = (a, b) => a - b;
console.log(arr.flat().unique().sort(sort));

// ES5
Array.prototype.flat = function() {
  return this.toString().split(',').map(v => Number(v));
}
Array.prototype.unique = function() {
  var obj = {}
  return this.filter((item, index) => {
    var tempItem = typeof item + JSON.stringify(item)
    return obj.hasOwnProperty(tempItem) ?
      false :
        obj[tempItem] = true
  })
}
const sort = (a, b) => a - b;
console.log(arr.flat().unique().sort(sort));

// ES5 或者
Array.prototype.flat = function() {
  return this.toString().split(',').map(v => Number(v));
}
function flat(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (item instanceof Array) {
      result.splice(result.length, 0, ...flat(item));
    } else {
      result.push(item);
    }
  }
  return result;
}
const sort = (a, b) => a - b;
console.log(flat(arr).unique().sort(sort));

⭐ 使用迭代的方式实现 flatten 函数

迭代

let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]]
const flatten = function(arr) {
  while (arr.some(v => Array.isArray(v))) {
     arr = [].concat(...arr)
  }
  return arr
}
console.log(flatten(arr));

递归

const flatten = function(arr) {
  return [].concat(...arr.map(v => Array.isArray(v) ? flatten(v) : v));
}
console.log(flatten(arr));

字符串拼接

function flatten(arr) {
  return arr.join(',').split(',').map(v => Number(v))
}

买卖股票的最佳时机

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
最大总利润为 4 + 3 = 7 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
最大总利润为 4 。

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0。
var arr = [7, 1, 5, 3, 6, 4]
function maxProfit(arr) {
    var income = 0;
    for (var i = 0, len = arr.length; i < len; i++) {
        var gap = arr[i + 1] - arr[i]; // 后一个与前一个比较,大于零则赚
        if (gap > 0) {
            income += gap;
        }
    }
    return income;
}
console.log(maxProfit(arr));

⭐️ 买卖股票的最佳时机 2

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路:

如果第i天卖出股票,则最大利润为 (该天的股价-前面天数中最小的股价),然后与已知的最大利润比较,如果大于则更新当前最大利润的值。

/**
 * @param {number[]} prices
 * @return {number} [7,3,2,3,1,7]
 */
var maxProfit = function(prices) {
    // 初始化: 收益 0
    let profit = 0, historyMinCost = prices[0];
    for (let i = 1; i < prices.length; i++) {
        // 假定当天卖出,与历史最小成本比较,算出当前最大收益
        // 再和历史最大收益比较
        const curProfit = prices[i] - historyMinCost;
        if (curProfit > profit) {
            profit = curProfit;
        }
        if (prices[i] < historyMinCost) {
            historyMinCost = prices[i];
        }
    }
    return profit;
};

⭐ 旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

输入: [1,2,3,4,5,6,7] 和 k = 3          输出: [5,6,7,1,2,3,4]

解释:

向右旋转 1 步: [7,1,2,3,4,5,6]

向右旋转 2 步: [6,7,1,2,3,4,5]

向右旋转 3 步: [5,6,7,1,2,3,4]

let arr = [1, 2, 3, 4, 5, 6, 7];

function rotate(arr, k) {
  for (let i = 0; i < k; i++) {
    let temp = arr.pop()
    arr.unshift(temp)
  }
  return arr;
}

console.log(rotate(arr, 3));
// 首先我们研究例子不难发现数组轮转k次实际上是将后k位数字移到了前面,而当k大于数组长度时,
// 我们只需要将k对数组长度取余即可,因为数组每进行了nums.length次轮转后又变回了原样

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var rotate = function (nums, k) {
  if (nums.length === 1) return;

  k = k % nums.length;
  const temp = nums.splice(nums.length - k, k);

  nums.unshift(...temp);
  return nums;
};

let arr = [1, 2, 3, 4, 5, 6, 7];
console.log(rotate(arr, 3));

判断数组是否存在重复

输入: [1,2,3,1]        输出: true

输入: [1,2,3,4]       输出: false

var arr = [1, 2, 3, 1]
function containsDuplicate(arr) {
    var obj = {}
    for (var i = 0, len = arr.length; i < len; i++) {
        if (obj[arr[i]]) {
            return true
        } else {
            obj[arr[i]] = true
        }
    }
    return false
}

// or

function containsDuplicate(arr) {
  return [...new Set(arr)].length !== arr.length
}

console.log(containsDuplicate(arr))

⭐ 找出只出现一次的数字

var arr = [4, 1, 2, 1, 2]

var arr = [4, 1, 2, 1, 2]
function singleNumber(arr) {
  for (let i = 0; i < arr.length; i++) {
    if (arr.indexOf(arr[i]) === arr.lastIndexOf(arr[i])) {
      return arr[i];
    }
  }
  return -1;
}
console.log(singleNumber(arr));

// or

function singleNumber(arr) {
    const a = [...new Set(arr)].reduce((total, cur) => total + cur, 0)
    const b = arr.reduce((total, cur) => total + cur, 0)
    return 2 * a - b
}

console.log(singleNumber(arr))

两个数组的交集

var nums1 = [1, 2, 3, NaN, {}]
var nums2 = [2, 2, 8, 10, NaN, {}]

// 方法1:filter + indexOf(不支持 NaN 和 {})
function intersect(nums1, nums2) {
  return nums1.filter(v => {
    const index = nums2.indexOf(v)
    if (index > -1) {
      nums2.splice(index, 1)
      return v
    }
  })
}
console.log(intersect(nums1, nums2))

// 方法2:filter + indexOf(支持 NaN,不支持 {})
function intersect(nums1, nums2) {
  const aHasNaN = nums1.some(v => isNaN(v))
  const bHasNaN = nums2.some(v => isNaN(v))
  return nums1.filter(v => {
    const index = nums2.indexOf(v)
    if (index > -1) {
      nums2.splice(index, 1)
      return v
    }
  }).concat(aHasNaN && bHasNaN ? [NaN] : [])
}
console.log(intersect(nums1, nums2))

// 方法3:哈希表(支持 NaN,{})
function intersect(nums1, nums2) {
  const map = {}
  const res = []
  for (let n of nums1) {
    if (map[n]) {
      map[n]++
    } else {
      map[n] = 1
    }
  }
  for (let n of nums2) {
    if (map[n] > 0) {
      res.push(n)
      map[n]--
    }
  }
  return res
}
console.log(intersect(nums1, nums2))

// 如果不要求输出一致,则可以用includes
function intersect(nums1, nums2) {
    return nums1.filter(v => nums2.includes(v))
}
console.log(intersect(nums1, nums2))

⭐ 加一

var arr = [9, 9, 9] // 预期:[1, 0, 0, 0]
function plusOne(arr) {
    var count = arr.length - 1;
    // 从末尾往前倒
    while (count > -1) {
        // 只要当前位+1大于9,就把当前位置为0,count--
        if (arr[count] + 1 > 9) {
            arr[count] = 0;
            count--;
        } else {
            // 一旦当前位+1不大于9,就放心+1,且直接退出,不用再算更高位的了
            arr[count]++;
            break;
        }
    }
    // 如果while后,第一位还是0,证明这个数组所有数字都为9.这个时候往数组最前面加个1就好
    if (arr[0] === 0) {
        arr.unshift(1);
    }
    return arr;
};
console.log(plusOne(arr))

// or

function plusOne(arr) {
  for (let i = arr.length - 1; i >= 0; i--) {
    const ele = arr[i];
    if (ele + 1 <= 9) {
      arr[i]++
      break
    } else {
      arr[i] = 0
    }
  }
  if (arr[0] === 0) {
    arr.unshift(1)
  }
  return arr
}

// or
var arr = [9, 9, 9] // 预期:[1, 0, 0, 0]
function fn(arr) {
  let num = parseInt(arr.join('')) + 1;
  let str = num.toString();
  return str.split('').map(v => Number(v));
}

console.log(fn(arr));

⭐ 移动零

var arr = [0, 1, 0, 3, 12] // 预期:[ 1, 3, 12, 0, 0 ]
var moveZeroes = function (nums) {
    // 长度提取出来
    var len = nums.length
    for (var i = 0; i < len; i++) {
        // 从头往后遍历,遇到0则删掉追放到尾部,
        // 同时让i--,因为头部删了个0;同时len--,因为不再判断追加后的
        if (nums[i] === 0) {
            nums.splice(i, 1)
            nums.push(0)
            i--
            len--
        }
    }
    return nums
}
console.log(moveZeroes(arr))

两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。

给定 var nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9

所以返回 [0, 1]

var nums = [2, 7, 11, 15], target = 9
function twoSum(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    const num = nums[i];
    // 第二个数索引
    const targetIndex = nums.indexOf(target - num);
    // 确保存在第二个数,且不为当前遍历的数
    if (targetIndex > -1 && targetIndex !== i) {
      return [i, targetIndex];
    }
  }
}
console.log(twoSum(nums, target))

⭐ 两个数组合并成一个数组

请把两个数组 ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']['A', 'B', 'C', 'D'],合并为 ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']

考察点:假设有一种情况,让你在一个列表中插入一个广告,不光是数组,对象依然有这种需求,这道题其实就是平常经常需要用到的一个小功能。

let arr1 = ["A1", "A2", "B1", "B2", "C1", "C2", "D1", "D2"]
let arr2 = ["A", "B", "C", "D"]
console.log(
	[...arr1, ...arr2].sort((v2, v1) => (
    v2.codePointAt(0) - v1.codePointAt(0) ||
    v1.length - v2.length ||
    v2.codePointAt(1) - v1.codePointAt(1)
  ))
)