这篇文章主要记录一下有关数组的api,利用这些api能解决那些问题,记录一下在项目中是如何用到他们的,以及一些经典题目解决。文章需要慢慢的补充。
mdn的顺序是根据字母排序的,这里会改变一下排序,把类似功能的放一块。
参考
mdn
js 改变原数组和不改变原数组的方法整理
解锁各种js数组骚操作,总有你想要的!
高阶函数
字符串和数组都有的方法
在学习的过程中,需要知道参数有哪些,返回值是什么,会不会改变原数组
push(), pop(), shift(), unshift(), reverse(),sort(),splice(),
copyWithin(),fill()
concat map filter slice。。
虽然可以遍历数组但是不建议,主要是遍历object类型
for in
for in 有时需要结合hasOwnProperty
但是for of 就不用
for of
for foreach;
map;
filter;
reduce
some,every;
for in , for of;
find,findindex;
values,keys,entries
参数
改变原数组吗
splice能够添加元素
Array.form ; Array.prototype.slice.call() ; …运算符
这里有很多方案
push, 三点运算符
推荐 新api(flat) arr.flat(Infinity);
法3
大体思路就是用递归,判断当前有没有数组 有的话就展开一层
展开的话 可以用flat 也可三点运算符展开, 也可用concat
如何判断是不是数组 那就又牵扯到其他的知识点了 首先可以用静态方法Array.isArray; 也可instanceof
但是实际上flat有一个可传入的参数 Infinity 可展开任意深度的嵌套数组
可用reduce
// 按年龄进行分类
var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
可用reduce
在数组中的浅拷贝有concat,slice 这两个都是返回数组的(如果数组中都是简单数据类型,可以说是深拷贝)
对象类型的话 有Object.assign()进行浅拷贝
关于深拷贝,
1.可以直接JSON.parse(JSON.stringify()) 先转成字符串 再转回来
2.手写 进行递归
3.工作场景直接用lodash就行
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
也可用Set这个新的数据结构,更加方便
'1-2-3'.split('-').map(item=>Number(item))
(3) [1, 2, 3]
可以将有点像数组却又不是数组的数据变成标准数组 比如
从类数组对象或者可迭代对象中创建一个新的数组实例
可将Map对象转换成一个二维键值对数组
set结构的数据变数组
let arrLike={
0:"es5",
1:"es6",
2:"es7",
length:3
}
console.log(Array.from(arrLike));
扩展 伪数组 伪数组是一个 Object, 而真实的数组是一个 Array
常见的伪数组: arguments, nodelist
一个应用场景 比如说现在要判断几个数里面的最大值, 写了一个函数 max(){…} 之后进行调用max(1,5,6,3,58,2) 如何拿到我传入的值呢 就可以用arguments
如何判断是不是伪数组? Array.isArray() ; 试着push一下看看能不能成功; xxx instance Array 看看是不是true //false
如何将伪数组转换成数组?
Array.from(arrLike)
Array.prototype.slice.call(arr1) / [].slice.call(arguments)
[…arguments]
用来判断某个变量是否是一个数组对象
根据一组参数来创建新的数组实例,支持任意的参数数量和类型
let arr=[1,'abc',true ,[3,5,8],{"age":12}]
let arr2=Array.of(1,'abc',true ,[3,5,8],{"age":12})
console.log(arr);
console.log(arr2);
参数 index
要返回的数组元素的索引(位置)。当传递负数时,支持从数组末端开始的相对索引;也就是说,如果使用负数,返回的元素将从数组的末端开始倒数。
返回值
匹配给定索引的数组中的元素。如果找不到指定的索引,则返回undefined。
使用场景 返回一个数组的最后一个值
这个例子比较了选择Array中倒数第二项的不同方法。虽然下面显示的所有方法都是可行的,但这个例子凸显了at()方法的简洁性和可读性。
// 数组及数组元素。
const colors = ['red', 'green', 'blue'];
// 使用长度属性。
const lengthWay = colors[colors.length-2];
console.log(lengthWay); // 'green'
// 使用 slice() 方法。注意会返回一个数组。
const sliceWay = colors.slice(-2, -1);
console.log(sliceWay[0]); // 'green'
// 使用 at() 方法。
const atWay = colors.at(-2);
console.log(atWay); // 'green'
字符串也有
返回一个新数组, 传入数组和/或值,不会改变原数组
var num1 = [1, 2, 3],
num2 = [4, 5, 6],
num3 = [7, 8, 9];
var nums = num1.concat(num2, num3);
console.log(nums);
// results in [1, 2, 3, 4, 5, 6, 7, 8, 9]
[1,2,3].concat(2)
(4) [1, 2, 3, 2]
[1,2,3].concat([1,2,5])
(6) [1, 2, 3, 1, 2, 5]
[1,2,3].concat(2,[1,2,5])
(7) [1, 2, 3, 2, 1, 2, 5]
[1,2,3].concat(2,[1,2,[5]])
(7) [1, 2, 3, 2, 1, 2, Array(1)]
object也有这个方法
不改变原数组
参数: 无
返回值:
iterator.next()返回一个对象,对于有元素的数组,
// 是next{ value: Array(2), done: false };
// next.done 用于指示迭代器是否完成:在每次迭代时进行更新而且都是false,
// 直到迭代器结束done才是true。
// next.value是一个[“key”,“value”]的数组,是返回的迭代器中的元素值。
迭代器以前了解过一些
.next()用法没有用过
这个特性很好用–使用for…of 循环
var arr = ["a", "b", "c"];
var iterator = arr.entries();
for (let e of iterator) {
console.log(e);
}
// [0, "a"]
// [1, "b"]
// [2, "c"]
扩展 for of 和 for in 区别
for of 还能用在哪里
for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
例子
//注意:这只能在实现了NodeList.prototype[Symbol.iterator]的平台上运行
let articleParagraphs = document.querySelectorAll("article > p");
for (let paragraph of articleParagraphs) {
paragraph.classList.add("read");
}
for(let [index,item]of arr.entries()){
console.log(index,item);
}
// 0 10
// 1 11
// 2 12
// 3 13
for of mdn文档
for in 遍历数组是有问题的 for of 就没有这个问题
Array.prototype.foo=function(){
console.log(foo);
}
for(index in arr){
console.log(index);
}
//0 1 2 3 4 5 6 foo
object也有这个方法
语法 arr.values()
返回值 方法返回一个新的 Array Iterator迭代 对象,该对象包含数组每个索引的值
没有参数
数组迭代器中存储的是原数组的地址 如果数组中元素改变,那么迭代器的值也会改变
使用 for...of 循环进行迭代 或者 .next()
let arr = ['w', 'y', 'k', 'o', 'p'];
let eArr = arr.values();
for (let letter of eArr) {
console.log(letter);
} //"w" "y "k" "o" "p"
警告:一次性:数组迭代器是一次性的,或者说临时对象
object也有这个方法
语法 arr.keys()
返回值 一个新的 Array 迭代器对象。
参数
callback 用来测试每个元素的函数,它可以接收三个参数:
element
用于测试的当前值。
index可选
用于测试的当前值的索引。
array可选
调用 every 的当前数组。
thisArg 执行 callback 时使用的 this 值。
返回值
如果回调函数的每一次返回都为 truthy 值,返回 true ,否则返回 false。
不改变原数组
特征: 当所有的元素都符合条件才会返回true。
[12, 5, 8, 130, 44].every(x => x >= 10); // false
参数
callback
用来测试每个元素的函数,接受三个参数:
element
数组中正在处理的元素。
index 可选
数组中正在处理的元素的索引值。
array可选
some()被调用的数组。
thisArg可选
执行 callback 时使用的 this 值。
返回boolean 数组中有至少一个元素通过回调函数的测试就会返回true;所有元素都没有通过回调函数的测试返回值才会为false。
不会改变数组。
示例
将任意值转换为布尔类型
var TRUTHY_VALUES = [true, 'true', 1];
function getBoolean(value) {
'use strict';
if (typeof value === 'string') {
value = value.toLowerCase().trim();
}
return TRUTHY_VALUES.some(function(t) {
return t === value;
});
}
getBoolean(false); // false
getBoolean('false'); // false
getBoolean(1); // true
getBoolean('true'); // true
这个api非常强大
语法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
参数
callback
执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:
accumulator
累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
currentValue
数组中正在处理的元素。
index 可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
array可选
调用reduce()的数组
initialValue可选
作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
返回值 函数累计处理的结果
不改变原数组
示例
数组里所有值的和
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
// 和为 6
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
return accumulator + currentValue.x;
},initialValue)
console.log(sum) // logs 6
将二维数组转化为一维
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
return a.concat(b);
},
[]
);
// flattened is [0, 1, 2, 3, 4, 5]
按属性对object分类
var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
var key = obj[property];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});
}
var groupedPeople = groupBy(people, 'age');
数组去重
备注: 如果你正在使用一个可以兼容Set 和 Array.from() 的环境, 你可以使用let orderedArray = Array.from(new Set(myArray)); 来获得一个相同元素被移除的数组。
let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myOrderedArray = myArray.reduce(function (accumulator, currentValue) {
if (accumulator.indexOf(currentValue) === -1) {
accumulator.push(currentValue)
}
return accumulator
}, [])
console.log(myOrderedArray)
--------------------
let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
let result = arr.sort().reduce((init, current) => {
if(init.length === 0 || init[init.length-1] !== current) {
init.push(current);
}
return init;
}, []);
console.log(result); //[1,2,3,4,5]
string可调用Array原型上的map
不会修改原数组
常用于重新格式化数组中的对象
比如说想只想要对象里的某些属性,或者是想改个属性名都可以用map来格式化
下面的例子演示如何在一个 String 上使用 map 方法获取字符串中每个字符所对应的 ASCII 码组成的数组:
var map = Array.prototype.map
var a = map.call("Hello World", function(x) {
return x.charCodeAt(0);
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
querySelectorAll 应用
下面代码展示了如何去遍历用 querySelectorAll 得到的动态对象集合。在这里,我们获得了文档里所有选中的选项,并将其打印:
var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
return obj.value;
});
一个坑
[“1”, “2”, “3”].map(parseInt);
我们期望输出 [1, 2, 3], 而实际结果是 [1, NaN, NaN].
添加链接描述
添加链接描述
parseInt(‘3’,2)将‘3’以2进制方式解析。。。二进制里只有0‘1’
["1", "2", "3"].map(parseInt)
应该对应的是:
[parseInt("1", 0), parseInt("2", 1), parseInt("3", 2)]
parseInt("3", 2) 的第二个参数是界于 2-36 之间的,之所以返回 NaN 是因为 字符串 "3" 里面没有合法的二进制数,所以 NaN。
我们还可以继续试验:
> ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"].map(parseInt)
[1, NaN, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
只有当第二个参数是 1 的时候返回 NaN,其它情况都返回 1。
> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"].map(parseInt)
[1, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 9, 11, 13, 15, 17, 19, 21]
简单列举一下:
parseInt("1", 0); // 十进制 1
parseInt("2", 1); // 第二个参数不在 2-36 直接
parseInt("3", 2); // 二进制 NaN
parseInt("4", 3); // 三进制
parseInt("5", 4);
parseInt("6", 5);
parseInt("7", 6);
parseInt("8", 7);
parseInt("9", 8);
parseInt("10", 9); // 九进制 (1*9+0 = 9)
parseInt("11", 10); // 十进制 (1*10+1 = 11)
parseInt("12", 11);
parseInt("13", 12);
parseInt("14", 13);
parseInt("15", 14);
parseInt("16", 15);
参数
callback 用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:
element
数组中当前正在处理的元素。
index可选
正在处理的元素在数组中的索引。
array可选
调用了 filter 的数组本身。
thisArg可选 执行 callback 时,用于 this 的值。
返回一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。
不改变原数组
项目中使用案例
这里是对搜索内容进行过滤。有两个搜索项,因为返回一个新的数组,可以对filter过的数据继续进行filter。 这样就能拿到符合搜索条件的项
扩展 那么这张图片的indexof 和 前面的include有什么区别呢 , 这两个方法在字符串和数组都是有的 , 一个是返回数值型的,一个是返回布尔型的,所以在if条件判断的时候includes要简单得多,而indexOf 需要多写一个条件进行判断。
下面先来看看数组,字符串类似
参数 : value,fromIndex
返回值: boolean
不改变原数组
字符串也有
fromIndex 如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。
类数组可以用
参数 value,fromIndex
返回值: 首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1
开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.
不改变原数组
字符串也有
找出指定元素出现的所有位置
参数
参数
callback
在数组每一项上执行的函数,接收 3 个参数:
element
当前遍历到的元素。
index可选
当前遍历到的索引。
array可选
数组本身。
thisArg可选
执行回调时用作this 的对象。
返回值 数组中第一个满足所提供测试函数的元素的值,否则返回 undefined。
不改变原数组
和上面的includes, indexof 相比 适合复杂场景 比如
用对象的属性查找数组里的对象
var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
function findCherries(fruit) {
return fruit.name === 'cherries';
}
console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
Copy to Clipboard
寻找数组中的质数
下面的例子展示了如何从一个数组中寻找质数(如果找不到质数则返回undefined)
function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) {
return false;
}
}
return element > 1;
}
console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
console.log([4, 5, 8, 12].find(isPrime)); // 5
和上面类似 只是返回的是index
查找数组中首个质数元素的索引
以下示例查找数组中素数的元素的索引(如果不存在素数,则返回-1)。
function isPrime(element, index, array) {
var start = 2;
while (start <= Math.sqrt(element)) {
if (element % start++ < 1) { // 从2开始,递加,当前传入值能不能整除 start 能-不是奇数 返回false
return false;
}
}
return element > 1;
}
console.log([4, 6, 8, 12].findIndex(isPrime)); // -1, not found
console.log([4, 6, 7, 12].findIndex(isPrime)); // 2
伪数组通过原型也可用
改变原数组
删除:shift,pop 返回被删除的元素
新增: push, unshift 返回length
改变原数组 在vue里面对数组的操作经常会遇到
参数:
start
指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
deleteCount 可选
整数,表示要移除的数组元素的个数。如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
item1, item2, … 可选
要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
返回值: 由被删除的元素组成的一个数组。
用在哪儿呢比如删除某一项,修改某一项,在特定index插入一个值都可以用这个api
从索引 2 的位置开始删除所有元素
var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2);
// 运算后的 myFish: ["angel", "clown"]
// 被删除的元素: ["mandarin", "sturgeon"]
不改变原数组
slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子。
数字可以直接进行比较
大小写混合可以调用string的toUpperCase toLowerCase 再进行比较
当排序非 ASCII 字符的字符串(如包含类似 e, é, è, a, ä 等字符的字符串)。一些非英语语言的字符串需要使用 String.localeCompare。这个函数可以将函数排序到正确的顺序。
var items = ['réservé', 'premier', 'cliché', 'communiqué', 'café', 'adieu'];
items.sort(function (a, b) {
return a.localeCompare(b);
});
// items is ['adieu', 'café', 'cliché', 'communiqué', 'premier', 'réservé']
JavaScript字符串排序localeCompare()笔记
添加链接描述
字符串是没有这个方法的 解决思路 转成数组 在调用此方法,在转成字符串
:String.prototype.split() 方法,Array.prototype.reverse() 方法和 Array.prototype.join() 方法。可链式调用
方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
改变原数组,
参数:arr.copyWithin(target[, start[, end]])
返回值:改变后的数组。
参数
value
用来填充数组元素的值。
start 可选
起始索引,默认值为0。
end 可选
终止索引,默认值为 this.length。
改变原数组
返回修改后的数组
let arr=new Array(3).fill(7)
console.log(arr); //[ 7, 7, 7 ]
let arr=[1,2,3,4,7,8]
arr.fill(5,2,4)
console.log(arr); //[ 1, 2, 5, 5, 7, 8 ]
在for循环里面break和continue是可以用的 但在forEach里面不能用
var prices = ['¥7', 500, 8123, 12];
prices.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' });
// "¥7,¥500,¥8,123,¥12"
const array1 = [1, 2, 'a', '1a'];
console.log(array1.toString());
// expected output: "1,2,a,1a"
Array对象覆盖了Object的 toString 方法。对于数组对象,toString 方法连接数组并返回一个字符串,其中包含用逗号分隔的每个数组元素。
当一个数组被作为文本值或者进行字符串连接操作时,将会自动调用其 toString 方法。
有一个常用的就是Object.prototype.toString.call() 用来判断是什么类型的数据, 调用Array本身的toString方法是不行的,因为他被改写了, 不能输出【object Object】类似的结果,而是输入由逗号分隔的字符串
扩展 字符串方法split – 字符串变数组
join – 数组变字符串
伪数组也可用
const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join());
// expected output: "Fire,Air,Water"
console.log(elements.join(''));
// expected output: "FireAirWater"
console.log(elements.join('-'));
// expected output: "Fire-Air-Water"
伪数组
function f(a, b, c) {
var s = Array.prototype.join.call(arguments);
console.log(s); // '1,a,true'
}
f(1, 'a', true);
flatMap从来都没有看到过
如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car);
// expected output: true
delete car.make;
if ('make' in car === false) {
car.make = 'Suzuki';
}
console.log(car.make);
// expected output: "Suzuki"
数组中的每个值可以是重复的,但是set里面的每一个值都是唯一的 这个就是set的特点
用处: 数组去重, 数组合并去重,求交集, 求差集
二维数组按行排序
碰到
// console.log(1111)
const obj = {
age: 20,
name: 'xxx',
address: {
city: 'beijing',
a:{
b:{
c:{
d:123
}
}
}
},
arr: ['a', 'b', 'c']
}
const obj1 = deepClone(obj)
obj1.address.a.b.c.d='789'
console.log(obj.address.a.b.c.d)
/**
*
* @param {Object} obj 要拷贝的对象
* @returns
*/
function deepClone(obj = {}) {
debugger;
// 不是对象 ,也不是数组 直接返回 null用两等其实就是===null或===undefined
if (typeof obj !== "object" || obj == null) {
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
// for in 遍历 / for of
for (let key in obj) {
// 判断key是不是自己所拥有的属性 保证key不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用
result[key] = deepClone(obj[key])
}
}
return result
}