数组去重,即从一个数组中移除所有重复的元素,确保每个元素只出现一次,是这一类问题的核心。
使用原生 JavaScript 方法
1. filter() 方法配合 indexOf()
const uniqueArray = array.filter((item, index, self) => {
return self.indexOf(item) === index;
});
该方法利用 filter() 遍历数组,对于每个元素,通过 indexOf() 查找其在原数组中的第一个索引。如果当前元素的索引与正在遍历的索引相同,说明这是该元素在数组中的首次出现,保留该元素;否则,忽略该元素。
2. reduce() 方法
const uniqueArray = array.reduce((acc, current) => {
return acc.includes(current) ? acc : [...acc, current];
}, []);
这里使用 reduce() 函数将数组累积到一个新的数组(acc)中。在每次迭代中,检查当前元素是否已存在于累积数组中。若不存在,则将其添加至累积数组;否则,跳过该元素。
利用 ES6 新特性
1. 使用扩展运算符与解构赋值
const uniqueArray = [...new Set(array)];
这种方法简洁高效,利用 ES6 的 Set 数据结构自动去除重复元素的特性,再通过扩展运算符将 Set 转换回数组。Set 是一种特殊的集合,不允许重复元素存在,因此插入过程会自动过滤重复项。
2. 利用 Map 数据结构
const uniqueArray = Array.from(new Map(array.map(item => [item, item])).values());
尽管不如直接使用 Set 直观,但此方法同样有效。它首先将数组映射为键值对相同的 Map,由于 Map 键的唯一性,重复的数组元素会被自动忽略。然后通过 Array.from() 和 Map.values() 将 Map 的值(即无重复元素)转换回数组。
双重循环与哈希表
1. 双重循环
const uniqueArray = [];
for (let i = 0; i < array.length; i++) {
let isDuplicate = false;
for (let j = 0; j < i; j++) {
if (array[i] === array[j]) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
uniqueArray.push(array[i]);
}
}
这种方法最直观也最基础,通过外层循环遍历数组,内层循环检查当前元素是否与之前的所有元素重复。如果没有重复,则将其添加到结果数组中。虽然理解简单,但时间复杂度较高,不适用于大型数据集。
2. 利用对象作为哈希表
const uniqueArray = [];
const hashTable = {};
for (let i = 0; i < array.length; i++) {
const item = array[i];
if (!hashTable[item]) {
uniqueArray.push(item);
hashTable[item] = true;
}
}
这种方法利用对象作为哈希表,以数组元素作为键。在遍历过程中,若元素尚未作为对象的键存在,则添加到结果数组并将其设置为哈希表的键。由于对象属性查找的时间复杂度接近 O(1),这种方法在处理大量数据时比双重循环更为高效。
性能比较与优化策略
1. 性能比较
filter()+indexOf():线性时间复杂度 O(n^2),适合小型数据集。reduce():线性时间复杂度 O(n^2),适合小型数据集。- 扩展运算符与
Set:近乎线性时间复杂度 O(n),非常高效,适合各种规模的数据集。 Map:近乎线性时间复杂度 O(n),非常高效,适合各种规模的数据集。- 双重循环:平方时间复杂度 O(n^2),效率低,仅适用于极小数据集。
- 哈希表:近乎线性时间复杂度 O(n),高效,适合各种规模的数据集。
2. 优化策略
- 选择合适的方法:根据数据规模和项目需求,优先考虑使用
Set、Map或哈希表方法,它们具有更高的时间效率。 - 预处理数据:如果可能,提前对数据进行排序或转换,简化去重逻辑,提高效率。
- 懒加载与分批处理:对于超大规模数据,可采用懒加载或分批处理策略,避免一次性加载全部数据导致的性能瓶颈。
- 使用 Web Worker:对于计算密集型的去重操作,可以考虑使用 Web Worker 进行多线程处理,避免阻塞主线程影响用户体验。
数组对象去重的方式,先看看数组对象的形式:
// 原数据是这样的 // 去重后数据是这样的
[{ [{
"goodsId": "1", "goodsId": "1",
"quota": 12, "quota": 12,
"skuId": "1" "skuId": "1"
}, },
{ {
"goodsId": "2", "goodsId": "2",
"quota": 12, "quota": 12,
"skuId": "2" "skuId": "2"
}, }]
{
"goodsId": "1",
"quota": 12,
"skuId": "1"
}]
1.使用filter和Map
代码简洁,好用,4行代码搞定,平均耗费时间最短,五星推荐
- 法一:
// 原数据是这样的 // 去重后数据是这样的
const arr = [{ // [{
"goodsId": "1", // "goodsId": "1",
"quota": 12, // "quota": 12,
"skuId": "1" //"skuId": "1"
}, //},
{ //{
"goodsId": "2", //"goodsId": "2",
"quota": 12, //"quota": 12,
"skuId": "2" //"skuId": "2"
}, //}]
{
"goodsId": "1",
"quota": 12,
"skuId": "1"
}]
function uniqueFunc (arr) {
let m = new Map();
return arr.filter((ele) => !m.has(ele.skuId) && m.set(ele.skuId, ""));
}
let arr2 = uniqueFunc(arr)
console.log('aee', arr2);
- 法二
// 方法一:
let map = new Map();
for (let item of arr) {
if (!map.has(item.skuId)) {
map.set(item.skuId, item);
};
};
console.log('map', map);
arr3 = [...map.values()];
console.log(arr3)
- 法三
let nArr = arr.filter((currentValue, currentIndex, selfArr) => {
return selfArr.findIndex(x => x.skuId === currentValue.skuId) === currentIndex
});
console.log('nArr', nArr);
2.使用reduce
代码稍多,平均耗费时间和第一不分伯仲,四星推荐
function uniqueFunc2(arr, uniId){
let hash = {}
return arr.reduce((accum,item) => {
hash[item[uniId]] ? '' : hash[item[uniId]] = true && accum.push(item)
return accum
},[])
}
3.使用for循环
耗费时间较一二稍多,但是耗费时间平均,三星推荐
function uniqueFunc3(arr, uniId){
let obj = {}
let tempArr = []
for(var i = 0; i<arr.length; i++){
if(!obj[arr[i][uniId]]){
tempArr.push(arr[i])
obj[arr[i][uniId]] = true
}
}
return tempArr
}
js获取数组中的最大值的9种方法
每种方法的工作原理和用法。以下是每种方法的详细说明:
1.方法1:Math.max()和apply()
-
使用Math.max()函数和apply()方法来找到数组中的最大值。
-
apply()方法将Math.max()函数应用于数组,它将数组的元素作为参数传递给Math.max()函数。
-
返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
const max = Math.max.apply(``null``, arr);
console.log(max); ``// 输出:5`
2.方法2:for循环遍历数组
- 使用for循环遍历数组,从第一个元素开始比较,找到最大值。
- 初始化一个变量max为数组的第一个元素。
- 从数组的第二个元素开始,依次与max比较,如果当前元素大于max,则更新max为当前元素。
- 返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
console.log(max); // 输出:5
3.方法3:reduce()方法
- 使用reduce()方法来迭代数组,并在每次迭代时找到最大值。
- 初始值设置为Number.MIN_SAFE_INTEGER,确保数组中的任何值都可以成为最大值。
- 在每次迭代时,使用Math.max()函数来比较当前元素和累加器的值,返回较大的值作为下一次迭代的累加器。
- 返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
const max = arr.reduce((acc, cur) => Math.max(acc, cur), Number.MIN_SAFE_INTEGER);
`console.log(max); ``// 输出:5`
4.方法4:sort()方法
- 使用sort()方法对数组进行排序。
- 默认情况下,sort()方法按照字符串顺序对元素进行排序。
- 通过提供一个比较函数,可以确保按照数字顺序对元素进行排序。
- 将数组的最后一个元素作为最大值返回。
const arr = [1, 3, 5, 2, 4];
arr.sort((a, b) => a - b);
const max = arr[arr.length - 1];
`console.log(max); ``// 输出:5`
5.方法5:spread语法和Math.max()函数
- 使用展开运算符(Spread Operator)将数组的元素作为参数传递给Math.max()函数。
- 返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
const max = Math.max(...arr);
console.log(max); // 输出:5
6.方法6:ES6的解构赋值和sort()方法
- 使用展开运算符(Spread Operator)将数组的元素作为参数传递给sort()方法进行排序。
- 使用解构赋值将排序后的数组的第一个元素赋值给变量max。
- 返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
const [max] = [...arr].sort((a, b) => b - a);
`console.log(max); ``// 输出:5`
7.方法7:ES6的解构赋值和reduce()方法
- 使用reduce()方法迭代数组,并在每次迭代时找到最大值。
- 初始值设置为Number.MIN_SAFE_INTEGER,确保数组中的任何值都可以成为最大值。
- 在每次迭代时,使用解构赋值将当前元素和累加器的值分别赋值给变量cur和max。
- 如果cur大于max,则更新max为cur。
- 返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
const [max] = arr.reduce(([max], cur) =>
cur > max ? [cur] : [max],
[Number.MIN_SAFE_INTEGER]);
console.log(max); // 输出:5
8.方法8:Math.max()函数和展开运算符(Spread Operator)
- 使用展开运算符(Spread Operator)将数组的元素作为参数传递给Math.max()函数。
- 返回数组中的最大值。
const arr = [1, 3, 5, 2, 4];
const max = Math.max(...arr);
`console.log(max); ``// 输出:5`
判断数据类型
常见的判断js数据类型的方法有如下几种
1.最常见的判断方法:typeof
2.已知对象类型: instanceof
3.对象原型链判断方法: prototype 通用但很繁琐
4.根据对象的构造器constructor进行判断
5.jQuery方法: jquery.type()
6.严格运算符: ===
一.typeof
其中typeof返回的类型都是字符串形式,需注意!!!!!
- 只能识别基础类型和引用类型
注意:
null、NaN、document.all的判断
alert(typeof "helloworld") ------------------>"string"
alert(typeof 123) ------------------>"number"
alert(typeof [1,2,3]) ------------------>"object"
alert(typeof new Function()) ------------------>"function"
alert(typeof new Date()) ------------------>"object"
alert(typeof new RegExp()) ------------------>"object"
alert(typeof Symbol()) ------------------>"symbol"
alert(typeof true) ------------------>"true"
alert(typeof null) ------------------>"object"
alert(typeof undefined) ------------------>"undefined"
alert(typeof 'undefined') ------------------>"string"
二.instance of
注意:instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
[1,2,3] instanceof Array -------->true
new Date() instanceof Date -------->true
new Function() instanceof Function -------->true
new Function() instanceof function -------->false
null instanceof Object -------->false
三.对象原型链判断方法: Object.prototype.toString.call()
适用于所有类型的判断检测,注意区分大小写. toString方法,在Object原型上返回数据格式
console.log(Object.prototype.toString.call("123")) -------->[object String]
console.log(Object.prototype.toString.call(123)) -------->[object Number]
console.log(Object.prototype.toString.call(true)) -------->[object Boolean]
console.log(Object.prototype.toString.call([1, 2, 3])) -------->[object Array]
console.log(Object.prototype.toString.call(null)) -------->[object Null]
console.log(Object.prototype.toString.call(undefined)) -------->[object Undefined]
console.log(Object.prototype.toString.call({name: 'Hello'})) -------->[object Object]
console.log(Object.prototype.toString.call(function () {})) -------->[object Function]
console.log(Object.prototype.toString.call(new Date())) -------->[object Date]
console.log(Object.prototype.toString.call(/\d/)) -------->[object RegExp]
console.log(Object.prototype.toString.call(Symbol())) -------->[object Symbol]
四.根据对象的constructor进行判断
constructor 判断方法跟instanceof相似,但是constructor检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型
- 语法:
obj instanceof Type - 功能:判断
obj是不是Type类的实例,只可用来判断引用数据 - 实现思路:
Type的原型对象是否是obj的原型链上的某个对象 - 注意:右操作数必须是函数或者 class
//注意当出现继承的时候,使用constructor会出现问题
function A() {};
function B() {};
A.prototype = new B(); //A继承自B
console.log(A.constructor === B) -------->false
var C = new A();
//现在开始判断C是否跟A的构造器一样
console.log(C.constructor === B) -------->true
console.log(C.constructor === A) -------->false
//解决这种情况,通常是手动调整对象的constructor指向
C.constructor = A; //将自己的类赋值给对象的constructor属性
console.log(C.constructor === A); -------->true
console.log(C.constructor === B); -------->false
五.有局限的判断:严格运算符===
通常===出现在我们的条件判断中,比如判断一个变量是否为空,变量是否为数据等,示例如下
var a = null;
typeof a //object
a === null //true
/*扩展补充*/
//判断一个非数组变量是否为空
if(typeof a != 'undefined' && a ){}
//判断一个数组变量是否为空
if (typeof a != “undefined” && a && a.length > 0) {}
总结
| 方法 | 基础数据类型 | 引用类型 | 注意事项 |
|---|---|---|---|
| typeof | √ | × | NaN、object、document.all |
| constructor | √ 部分 | √ | 可以被改写 |
| instanceof | × | √ | 多窗口,右边构造函数或者class |
| isPrototypeof | × | √ | 小心 null 和 undefined |
| toString | √ | √ | 小心内置原型 |
| Symbol.toString Tag | × | √ | 识别自定义对象 |
| 等比较 | √ | √ | 特殊对象 |
数组里面的对象合并
let obj = [
{ name1: '张三', age1: "12" },
{ name2: '李四', age2: "14" },
{ name3: '王五', age3: "30" }
]
let newObj = {} // 新建一个对象
obj.forEach((item) => {
newObj = { ...newObj, ...item } // 要把自己放进去
})
console.log(newObj) // { name1: "张三", age1: "12", name2: "李四", age2: "14", name3: "王五", age3: "30" }
ps:工作中用过的一些知识点,自己记录的笔记和整理来网络上的资源 ,方便以后复习使用。