关于数组去重

【本文源址:http://blog.youkuaiyun.com/q1056843325/article/details/73277063 转载请添加该地址】

JavaScript数组去重问题虽然在实际开发中很少应用 
但却是面试(或笔试)中可能被问到的题 
如果只能说出一两种方法的话,就显得我们很low 
所以这里我总结了数组去重的六种方法供大家参考

直接遍历

首先先来一种简单粗暴的方法 
也是很容易就可以想到的办法 
声明一个新数组 
直接遍历这个待去重数组 
然后把新数组中没有的元素推进去

function unique(arr){
  var newArr = [];
  for(var i = 0, item; item = arr[i++];){
    if(newArr.indexOf(item) === -1){
      newArr.push(item);
    }
  }
  return newArr;
};

测试数组

var arr = [1,3,2,1,4,5,2,4,1,5];
console.log(unique(arr)); //[1,3,2,4,5]

这个结果是正确的 
但是如果元素中有undefined或者null就会出现问题

var arr = [1,3,2,1,null,4,5,2,4,1,5,null];
console.log(unique(arr)); //[1,3,2]

这是因为for(var i = 0, item; item = arr[i++];) 
null导致提前跳出了循坏 
没办法虽然我很喜欢这种循环方式 
但是为了保持严谨还是要使用正常的for循环

function unique(arr){
  var newArr = [];
  var item;
  for(var i = 0, len = arr.length; i < len; i++){
    item = arr[i];
    if(newArr.indexOf(item) === -1){
      newArr.push(item);
    }
  }
  return newArr;
};
var arr = [1,3,2,1,null,4,5,2,4,1,5,null];
console.log(unique(arr)); //[1,3,2,null,4,5]

当然也可以使用ES5的forEach方法 
虽然它没有for循环的效率高

function unique(arr){
  var newArr = [];
  arr.forEach(function(item){
    if(newArr.indexOf(item) === -1){
      newArr.push(item);
    }
  });
  return newArr;
}

下面的其他方法也可以把for循环替换成forEach


除此之外,我们还可以使用ES5的reduce方法 
让代码看起来更加高大上

function unique(arr){
  return arr.reduce(function(prev, next){
    if(prev.indexOf(next) === -1){
      prev.push(next);
    }
    return prev;
  }, []);
}

indexOf也可以替换成循环判断 
不过既然有好使的API就直接拿来用了 
顺便一提,方法不能够处理数组NaN的去重

var arr = [4, 2, 1, 3, 2, 3, NaN, NaN];
console.log(unique(arr)); //[4,2,1,3,NaN,NaN]

包括下面的方法都不可以 
不过可以把indexOf()换成ES6的Array.contains() 
真正意义上的绝对相等 
这里我就不讨论太复杂的情况了

索引判断

这种方法是利用了数组indexOf的特点 
它会找到数组中第一个该元素值的索引 
所以我们可以判断数组元素的indexOf索引判断和元素本身的索引是否相同 
如果相同,代表这是数组第一次出现的该元素值

function unique(arr){
  var newArr = [];
  var item;
  for(var i = 0, len = arr.length; i < len; i++){
    item = arr[i];
    if(arr.indexOf(item) === i){
      newArr.push(item);
    }
  }
  return newArr;
}

我们还可以做一些小小的优化 
因为实际上第一次是不需要判断的 
数组元素的第一个值一定是首次出现的 
所以从数组的第二个元素开始循环就可以了

function unique(arr){
  var newArr = [arr[0]];
  var item;
  for(var i = 1, len = arr.length; i < len; i++){
    item = arr[i];
    if(arr.indexOf(item) === i){
      newArr.push(item);
    }
  }
  return newArr;
}

排序去邻

这种方法的原理就是首先调用数组的sort方法 
我们的目的不是给数组元素排序 
所以也不需要为sort添加处理函数 
目的是为了把相同的元素值聚在一起 
这样只需要判断数组元素值和上一个索引值不同就可以了

function unique(arr){
  var newArr = [arr[0]];
  var item;
  arr.sort();
  for(var i = 1, len = arr.length; i < len; i++){
    item = arr[i];
    if(item !== arr[i - 1]){
      newArr.push(item);
    }
  }
  return newArr;
}

优化遍历

这种方法看起来特别梦幻 
原理是不断的将数组最右边不重复的值推入新数组

function unique(arr){
  var newArr = [];
  for(var i = 0, len = arr.length; i < len; i++){
    for(var j = i + 1; j < len; j++){
      if(arr[i] === arr[j]){
        j = ++i;
      }
    }
    newArr.push(arr[i]);
  }
  return newArr;
};

说的再详细一下 
就是对数组的每一个元素都进行判断(指针i) 
还有另一个指针从判断元素的下一位进行判断 
移动这个指针(指针j下移) 
如果发现判断元素与指针指向的值相等 
证明该判断元素不是数组中唯一的 
那么就继续往下判断(指针i下移,指针j回到i的下一位) 
直到j移到数组终点 
证明判断元素(指针i指向的元素)是数组中唯一的 
推入新数组

临时对象

这种方法是很好的一种方法 
借用了一个临时对象的数据结构 
这个对象用来存储数组的元素

function unique(arr){
  var newArr = [];
  var temp = {};
  var item;
  for(var i = 0, len = arr.length; i < len; i++){
    item = arr[i];
    if(!temp[item]){
      temp[item] = true;
      newArr.push(item);
    }
  }
  return newArr;
}

这是典型的空间换取时间思想的算法 
如果数组很大可能会很占内存 
但是效率很高这是毋庸置疑的 
其实现在这个函数还是有缺点

var arr = [1,3,2,1,4,5,2,4,1,5,'1','2'];
console.log(unique(arr)); //[1,3,2,4,5]

从这个测试中可以看到数组和字符没能有效区分 
这是因为它们传入对象中会调用toString() 
对象的键都是字符串 
既然这样我们可以对函数做一些修改 
让对象的键对应一个数组 
数组存储着已有的类型

function unique(arr){
  var newArr = [];
  var temp = {};
  var item;
  var type;
  for(var i = 0, len = arr.length; i < len; i++){
    item = arr[i];
    type = typeof item;
    if(!temp[item]){
      temp[item] = [type];
      newArr.push(item);
    }else if(temp[item].indexOf(type) === -1){
      temp[item].push(type);
      newArr.push(item);
    }
  }
  return newArr;
}

这样内存占用更大了 
但是却更加严谨了

var arr = [1,3,2,1,4,5,2,4,1,5,'1','2'];
console.log(unique(arr)); //[1,3,2,4,5,"1","2"]

集合转换

集合这个数据结构最大的特点就是 
集合内部的元素都是唯一的 
ES6标准给我们提供了Set集合 
利用这个新的数据结构我们可以很容易的实现数组去重 
容易到什么程度呢? 
一行.. 
就够了

function unique(arr){
  return Array.from(new Set(arr));
}

这才是真正的简单粗暴 
将数组转为集合,抛弃多余数值 
最后利用ES6的新数组方法Array.from将集合转为数组返回

 
 

function unique(arr){returnArray.from(new Set(arr)); }

转载于:https://www.cnblogs.com/llfy/p/9476115.html

<think>首先,用户的问题是:“我想使用React对数组进行处理 请问react数组方法”。用户想了解如何在React中对数组进行。 在系统级指令中,我需要遵循一些规则: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。 另外,用户提供了参考站内引用: - 引用[1]:关于React中使用dedupe库进行数组的例子。 - 引用[2]:关于ReactJS中对象数组的实现代码。 用户的问题是关于React数组的,所以我应该提供在React环境中处理数组的方法。React本身不提供特定的数组方法,但我们可以使用JavaScript的数组方法来实现。 常见的数组方法包括: 1. 使用Set:对于简单数组(如数字或字符串),使用Set可以轻松。 2. 使用filter和indexOf:对于不支持Set的环境或需要兼容性时。 3. 对于对象数组,需要自定义逻辑,比如基于特定属性。 用户提供的引用[1]展示了使用dedupe库,引用[2]展示了手动实现对象数组。 在回答中,我应该: - 解释基本方法。 - 提供代码示例。 - 确保代码是React友好的,比如在组件中使用。 - 生成相关问题。 结构化回答: 1. **介绍**:简要说明React数组的基本概念。 2. **方法**: - 简单数组(使用Set)。 - 简单数组(使用filter)。 - 对象数组(基于特定属性)。 3. **代码示例**:提供React组件中的示例代码。 4. **生成相关问题**:在最后添加相关问题。 引用处理: - 在回答中,如果引用用户提供的引用,末尾添加引用标识如[^1]。 - 用户提供的引用是关于的例子,我可以参考它们,但需要确保内容相关。 用户真正的需求是React数组,所以我应该聚焦于如何在React应用中实现。 最终回答用中文。 生成相关问题: - 例如:如何优化性能?如何在React状态管理中处理?等。 大纲回答: 1. 开头:解释React数组要性。 2. 方法1:简单数组(使用Set)。 3. 方法2:简单数组(使用filter)。 4. 方法3:对象数组。 5. 完整React组件示例。 6. 相关问题。</think>### React数组方法详解 在React开发中,数组是常见需求,例如处理从API获取的数据或用户输入列表。React本身不提供内置函数,但你可以利用JavaScript原生方法实现。下面我将分步解释不同场景下的方法,并提供React组件代码示例。所有方法均遵循ES6语法,确保高效和可维护性。 #### 1. **简单数组(元素为基本类型)** 对于数字、字符串等基本类型数组,最简洁的方法是使用`Set`对象。`Set`自动复值,然后通过`Array.from()`或扩展运算符转换回数组。 - **代码示例(在React函数组件中使用)**: ```jsx import React, { useState, useEffect } from 'react'; function SimpleArrayDedupe() { const [data, setData] = useState([1, 2, 2, 3, 4, 4]); const [uniqueData, setUniqueData] = useState([]); useEffect(() => { // 使用Set const deduped = [...new Set(data)]; setUniqueData(deduped); }, [data]); return ( <div> <p>原始数组: {data.join(', ')}</p> <p>数组: {uniqueData.join(', ')}</p> </div> ); } ``` - **说明**:此方法时间复杂度为$O(n)$,高效简洁。适用于React状态更新或渲染列表。 如果环境不支持ES6(如旧浏览器),可使用`filter`和`indexOf`: ```javascript const deduped = data.filter((item, index) => data.indexOf(item) === index); ``` #### 2. **对象数组(基于特定属性)** 当数组元素是对象时(例如`[{id: 1, name: 'A'}, {id: 1, name: 'B'}]`),需要基于唯一属性(如`id`或`key`)。引用[2]提供了手动实现方法,但我们可以优化为更简洁的版本。 - **代码示例(使用`reduce`或`Map`)**: ```jsx import React from 'react'; function ObjectArrayDedupe() { const originalData = [ { key: 1, value: 'React' }, { key: 2, value: 'Vue' }, { key: 1, value: 'React' }, // 复项 { key: 3, value: 'Angular' } ]; // 方法1: 使用reduce(基于key属性) const dedupedWithReduce = originalData.reduce((acc, current) => { const exists = acc.find(item => item.key === current.key); if (!exists) acc.push(current); return acc; }, []); // 方法2: 使用Map(更高效) const dedupedWithMap = Array.from( new Map(originalData.map(item => [item.key, item])).values() ); return ( <div> <p>原始数据长度: {originalData.length}</p> <p>后长度 (reduce): {dedupedWithReduce.length}</p> <p>后长度 (Map): {dedupedWithMap.length}</p> </div> ); } ``` - **说明**:`Map`方法时间复杂度为$O(n)$,性能最优。适用于React中处理动态数据源,如从API获取的列表[^2]。 #### 3. **使用第三方库(如dedupe)** 对于复杂场景,可以使用轻量级库如`dedupe`(引用[1]示例)。先安装库: ```bash npm install dedupe ``` - **代码示例**: ```jsx import React, { useEffect } from 'react'; import dedupe from 'dedupe'; function DedupeLibraryExample() { const list = [1, 2, 2, 3]; const uniqueList = dedupe(list); // 结果: [1, 2, 3] useEffect(() => { console.log('结果:', uniqueList); }, []); return <div>检查控制台输出结果</div>; } ``` - **说明**:库方法简化代码,但增加依赖。适合大型项目或需要高级逻辑时[^1]。 #### 最佳实践建议 - **性能考虑**:在React中,避免在渲染函数中直接,应使用`useEffect`或`useMemo`缓存结果,防止复计算。 - **状态管理**:如果数组来自React状态(如`useState`),后更新状态以触发新渲染。 - **数学表达示例**:算法的时间复杂度可表示为$O(n)$(线性时间),其中$n$是数组长度,确保高效性。 ### 相关问题 1. 如何在React中优化大型数组的性能? 2. 后如何与React状态管理(如Redux)集成? 3. 对象数组时,如何处理嵌套属性的唯一性? 通过以上方法,你可以灵活处理React中的数组需求。如果有具体数据示例,可进一步优化代码!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值