本文摘自PHP中文网,作者coldplay.xixi,侵删。
目录
- 一、双层循环(暴力方法)
- 二、indexOf和includes
- 三、排序去重
- 四、filter
- 五、键值对(key-value)
- 六、ES6
- 七、一些问题
- 参考
写在最后
(相关免费学习推荐:javascript视频教程)
一、双层循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | const unique = (arr)=>{
for (let i = 0; i < arr.length; i++){
for (let j = i + 1; j < arr.length; j++){
if (arr[i] === arr[j]){
arr.splice(j, 1);
j--;
}
}
}
return arr;}; const unique = (arr)=>{
var arr = [1, '1' , '1' , 1, 2, true, false, true, 3, 2, 2, 1];
var newArr = [];
for (let i = 0; i < arr.length; i++){
for ( var j = 0; j < newArr.length; j++){
if (arr[i] === newArr[j]) break ;
}
if (j === newArr.length){
newArr.push(arr[i]);
}
}
return newArr;}unique([1, '1' , '1' , 1, 2, true, false, true, 3, 2, 2, 1]);
|
核心点:
- 时间复杂度:
O(n^2)
- 上面的两种方法都是两次循环遍历,处理方式略有不同
- 上述实现方式确实不是最佳选择,但它胜在兼容性好啊~
二、indexOf和includes
2.1 indexOf简化一层循环判断
核心点:
- 如果需要返回原数组,则可以在
indexOf
方法找到重复项时(不等于它首次出现的位置)时利用splice
移除 indexOf
:返回在数组中可以找到一个给定元素的第一个索引
,如果不存在,则返回-1。indexOf(ele, fromIndex)
- ele:要查找的元素
- fromIndex:查找元素的起始位置,默认为0,允许负数,-2表示从倒数第二个元素开始查找
- 返回一个下标(number)
代码:
1 2 3 4 5 6 7 8 | const unique = (arr) => {
var res = [];
for (let i = 0; i < arr.length; i++){
if (res.indexOf(arr[i]) === -1 ){
res.push(arr[i]);
}
}
return res;}
|
2.2 includes简化一层循环判断
核心点:
- 具体的是要返回原数组还是新数组大家可以自行组合~
includes
:用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true
,否则返回false
includes(ele, fromIndex)
- ele:要查找的元素
- fromIndex:指定索引处开始查找。默认为 0,如果为负值,从末尾开始往前跳
fromIndex
的绝对值个索引。。 - 返回结果(bool)
代码:
1 2 3 4 5 6 7 8 | const unique = (arr) => {
var res = [];
for (let i = 0; i < arr.length; i++){
if (!res.includes(arr[i])){
res.push(arr[i]);
}
}
return res;}
|
2.3 indexOf和includes对于当前场景的选择
在这里我们推荐使用includes来查找元素:
返回值可以直接作为if的条件语句,简洁
1 | if (res.indexOf(arr[i]) !== -1 ){ todo }
|
识别NaN
如果数组中有NaN
,你又正好需要判断数组是否有存在NaN
,这时你使用indexOf
是无法判断的,你必须使用includes
这个方法。
1 | var arr = [NaN, NaN];arr.indexOf(NaN);
|
识别undefined
如果数组中有undefined
值,includes
会认为空的值是undefined,而indexOf
不会。
1 | var arr = new Array(3);console.log(arr.indexOf(undefined));
|
三、排序去重
核心点:
- 数组排序后,相同的元素会相邻,所以如果当前元素与它的相邻元素不同,就存入到新数组中;
- 相比于indexOf,只需要一层循环;
- concat会拼接数组,并返回新数组;
- sort()排序是通过按照转换为的
字符串的各个字符的Unicode位点
进行排序。所以很难保证它的准确性;
代码:
1 2 3 4 5 6 7 8 9 10 11 12 | var arr = [1, 1, '1' ]; function unique(arr) {
var res = [];
var sortedArr = arr.concat().sort();
var last;
for ( var i = 0; i < sortedArr.length ; i++) {
if (!i || last !== sortedArr[i]) {
res.push(sortedArr[i])
}
last = sortedArr[i];
}
return res;}console.log(unique( array ));
|
四、filter
核心点:
- filter:方法创建一个新数组, 其包含通过所提供
函数实现的测试
的所有元素(返回测试函数成立的元素) filter(callback, thisArg)
:- callback接受三个参数:element-当前正在处理的元素,index-当前元素索引,array-调用了filter的数组本身
- thisArg:执行 callback 时,用于 this 的值。
利用filter我们可以在代码层面简化一下外层循环:
代码:
1 2 3 4 5 | var arr = [1, 2, 1, 1, '1' ]; const unique = function (arr) {
var res = arr.filter( function (item, index, arr){
return arr.indexOf(item) === index;
})
return res;}console.log(unique(arr));
|
结合排序的思路:
1 2 3 4 | var arr = [1, 2, 1, 1, '1' ]; const unique = function (arr) {
return arr.concat().sort().filter( function (item, index, arr){
return !index || item !== arr[index - 1]
})}console.log(unique(arr));
|
五、键值对(key-value)
前面提到的几种方式总结下来大致分为
- 非排序数组,两次遍历判断(遍历、查询)
- 排序数组,相邻元素比较
我们再提出一种方式,利用Object对象 key-value的方式,来统计数组中元素出现的个数,初步判断逻辑也有两种
拿[1,1,1,2,2,3,'3']
举例:
- 统计每个元素出现的次数,obj:{1: 3, 2: 2, 3: 3}, 返回这个
obj
的key
而不管他们的value
- 只元素首次出现,再次出现则证明他是重复元素
5.1 统计次数
1 2 3 4 5 6 7 8 9 10 | var arr = [1, 2, 1, 1, '1' , 3, 3]; const unique = function (arr) {
var obj = {};
var res = [];
arr.forEach(item => {
if (!obj[item]) {
obj[item] = true;
res.push(item);
}
});
return res;}console.log(unique(arr));
|
5.2 结合filter
1 2 3 4 5 | var arr = [1, 2, 1, 1, '1' ]; const unique = function (arr) {
var obj = {};
return arr.filter( function (item, index, arr){
return obj.hasOwnProperty(item) ? false : (obj[item] = true)
})}console.log(unique(arr));
|
5.3 key: value存在的问题
对象的属性是字符串类型的,即本身数字1
和字符串‘1’
是不同的,但保存到对象中时会发生隐式类型转换,导致去重存在一定的隐患。
考虑到string和number的区别(typeof 1 === ‘number’, typeof ‘1’ === ‘string’),
所以我们可以使用 typeof item + item
拼成字符串作为 key 值来避免这个问题:
1 2 3 4 5 6 7 8 9 10 | var arr = [1, 2, 1, 1, '1' , 3, 3, '2' ]; const unique = function (arr) {
var obj = {};
var res = [];
arr.forEach(item => {
if (!obj[typeof item + item]) {
obj[typeof item + item] = true;
res.push(item);
}
});
return res;}console.log(unique(arr));
|
六、ES6
随着 ES6 的到来,去重的方法又有了进展,比如我们可以使用 Set 和 Map 数据结构。
6.1 Set
Set:它允许你存储任何类型的唯一值,无论是原始值或者是对象引用
代码:
1 2 | var arr = [1, 2, 1, '1' , '2' ]; const unique = function (arr) {
return Array.from( new Set(arr));}console.log(unique(arr));
|
简化1:
1 2 | function unique( array ) {
return [... new Set( array )];}
|
简化2:
1 | var unique = (a) => [... new Set(a)]
|
6.2 Map
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
- Map.prototype.has(key):返回一个布尔值,表示Map实例是否包含键对应的值。
- Map.prototype.set(key, value):设置Map对象中键的值。返回该Map对象。
1 2 3 | function unique (arr) {
const newMap = new Map()
return arr.filter((a) => !newMap.has(a) && newMap.set(a, 1));}
|
写到这里比较常规的数组去重方法就总结的差不多了,如果需要更强大的去重方法,我们需要对他们进行组合,而且因为场景的不同,我们所实现的方法并不一定能涵盖到
相关免费学习推荐:javascript(视频)
以上就是JavaScript专题之二:数组去重的详细内容,更多文章请关注木庄网络博客!
相关阅读 >>
javascript专题之七:类型转换
javascript中怎么将字符串转为数组
javascript怎么执行cmd命令
javascript中的栈、队列操作
详解javascript中的函数柯理化
javascript如何获取字符串中出现多次的字符(代码)
深入了解javascript中的null
浅谈使用javascript如何进行ajax调用和请求
javascript作用有哪些
如何解决javascript乱码问题
更多相关阅读请进入《javascript》频道 >>
人民邮电出版社
本书对 Vue.js 3 技术细节的分析非常可靠,对于需要深入理解 Vue.js 3 的用户会有很大的帮助。——尤雨溪,Vue.js作者
转载请注明出处:木庄网络博客 » JavaScript专题之二:数组去重