20中数组去重的方法20种数组去重的方法

article/2025/9/6 6:52:08

开始

本文有很多问题,并没有直接给出答案,大伙有自己思考的可以评论区留言。关于时间复杂度只是一个大体的估计。20种只能说保守了20种都是单论思路而已,暂时没想到更多的思路,有其他方法的可以评论区留言。

easy模式

此时我们有一个极其简单的数组,它可能包含也不包含重复项。我们需要删除重复项并将唯一值放入新数组中。

const names = ["a","b","c","d","e","a","b"];

new Set

时间复杂度:O(n^2), 但扩展符运算符耗费时间有点多,一般推荐

最简单的,new Set去重


let newNames = [...new Set(names)]

new Set

时间复杂度:O(n^2), 比扩展运算符还费劲,一般推荐

let newNames = Array.from(new Set(names))

new Map

时间复杂度:O(n^2), 一般推荐 通过new Map原型上的方法去重。

function dealArr (a) {let newArr = []let map = new Map()for(let i = 0;i<a.length;i++){if (!map.has(a[i])) {map.set(a[i], true)newArr.push(a[i])}};return newArr
}

笨蛋双重for循坏

感觉实在没什么好说的,就纯暴力去重

function dealArr(a){let len = a.length;for(let i = 0; i < len; i++) for(let j = i + 1; j < len; j++) if(a[j] == a[i]){a.splice(j,1);j--;len--;}return a;
}

 
function dealArr(a) {let b = [];for (let i = a.length - 1; i >= 0; i--) {for (let j = a.length - 1; j >= 0; j--) {if (a[i] == a[j] && i != j) {delete a[j];}}if (a[i] != undefined) b.push(a[i]);}return b;
}

单for循坏

时间复杂度:O(n), 它真的太快了,它是所有种类的方法里最快的,大伙可以试一试, 推荐

for循坏+hash查找

function dealArr(a) {let obj = {};let out = [];let len = a.length;let j = 0;for(let i = 0; i < len; i++) {let item = a[i];if(obj[item] !== 1) {obj[item] = 1;out[j++] = item;}}return out;
}

下面这种会快一点。

function dealArr(a) {obj = {};for (let i = 0; i < a.length; i++) {obj[a[i]] = true;}return Object.keys(obj);
}

for and includes

时间复杂度:O(n^2), 不推荐

for循环 + includes判断,includes会循坏到找到为止或者全部,所以挺慢的。

function dealArr(a) {let newArr = [];let j = 0;for (i = 0; i < a.length; i++) {let current = a[i];if (!newArr.includes(current)) newArr[j++] = current;}return newArr;
}

for and indexOf

时间复杂度:O(n^2), 不推荐

for循环 + indexof查找,indexOf会找到第一个为止或者全部。

function dealArr(a) {let newArr = [];let j = 0;for (i = 0; i < a.length; i++) {let current = a[i];if (newArr.indexOf(current) < 0) newArr[j++] = current;}return newArr;
}

for and lastIndexOf

时间复杂度:O(n^2), 不推荐

没啥好说的,其实和indexOf一样只是正反序查找的区别而已,问就是慢

function dealArr(a) {let newArr = [];let j = 0;for (i = 0; i < a.length; i++) {let current = a[i];if (newArr.lastIndexOf(current) < 0) newArr[j++] = current;}return newArr;
}  

for and newArr

相比哈希也慢

一个新数组和原数组对比,不同则放在新数组,最后返回。

function dealArr(a) {let newArr = [a[0]];for (let i = 1; i < a.length; i++) {let repeat = false;for (let j = 0; j < newArr.length; j++) {if (a[i] === newArr[j]) {repeat = true;break;}}if (!repeat) {newArr.push(a[i]);}}return newArr;
}

for and sort

想想有什么问题

先将原数组排序,再与相邻的进行比较,如果不同则存入新数组。

function dealArr(a) {let formArr = a.sort()let newArr=[formArr[0]]for (let i = 1; i < formArr.length; i++) {if (formArr[i]!==formArr[i-1]) newArr.push(formArr[i])}return newArr
}

splice

O(n^2),特别慢

function dealArr(arr) {let i,j,len = arr.length;for (i = 0; i < len; i++) {for (j = i + 1; j < len; j++) {if (arr[i] == arr[j]) {arr.splice(j, 1);len--;j--;}}}return arr;
}

filter and indexOf

时间复杂度:O(n^2)一般推荐

filter的本质相当于,在每一个元素上添加检查检查该元素在数组中的第一个位置是否等于当前位置indexof是找到第一个符合条件的元素。重复元素在数组里的位置是与找到的第一个不同的。

let newNames = names.filter(function(item, index) {return names.indexOf(item) == index;
})

但其实上述方法不是很好,因为可能你会操作到原数组,导致原数据变化,那么我们可以直接用filter的第三个参数来做这件事,保证原数据的不可变性。

let newNames = names.filter(function(item, index, self) {return self.indexOf(item) == index;
})

filter and sort

时间复杂度:O(n)- O(n^2)不推荐

就是先对数组进行排序,然后删除与前一个元素相等的每个元素。大家也可以想想这方法有啥问题。提示:排序。

  let newNames =  a.sort().filter(function(item, index, self) {return !index || item != self[index - 1];});

reduce

实在是太慢了,不推荐

reduce果然是js里最完美的api

let newNames = names.reduce(function(a,b){if (a.indexOf(b) < 0 ) a.push(b);return a;},[]);

笨蛋hashMap

时间复杂度:O(n)一般

这个方法有点笨,哈希表查找fiter,大伙可以想一想缺陷是什么。提示:对象,key。 测试用例: [1, '1']。)

function dealArr(a) {let seen = {};return a.filter(function(item) {return seen.hasOwnProperty(item) ? false : (seen[item] = true);});
}

Normal模式

easy模式下我们都只处理一些基数组,接下来我们处理一下数组对象+基数组

一般聪明的hashMap

一般聪明的hash,大伙看到应该能明白上面的问题是什么了吧。经过一点点优化,我们对原始值和引用对象开处理,到这它已经有了处理对象引用重复的能力了,但是它确实还不够聪明

就像这样

function dealArr(a) {let prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];return a.filter(function(item) {let type = typeof item;if(type in prims)return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);elsereturn objs.indexOf(item) >= 0 ? false : objs.push(item);});
}

聪明的hashMap

我们有时候可以写一个通用的函,通回调函数来优雅的完成过滤,比如这样!

大家可以思考一下为什么JSON.stringify,能完成过滤。

function dealArrByBey(a, key) {let obj = {};return a.filter(function(item) {let k = key(item);return obj.hasOwnProperty(k) ? false : (obj[k] = true);})
}

稍微炫一点

但这都es6了还这么玩不太合适,这样会好看一些。

可以看到过滤了后面的b.

function dealArrByBey(a, key) {let obj = new Set();return a.filter(item => {let k = key(item);return obj.has(k) ? false : obj.add(k);});
}

特别炫

感觉这么写就特别开心了,虽然可读性不好,而且也不是很快,它很帅啊。但这三种方法,是有点区别的,上面两种方法是保留第一个,过滤掉后面的,而这种方法保留的是最后一个,大伙可以思考一下为什么

function dealArrByBey(a, key) {return [...new Map(a.map(x => [key(x), x])).values()]
}

hard模式

珂里化 + 链式调用

em,都写到这了,我们可以再进阶再抽象一下,让我们的去重也可以写成一个非常抽象的链式调用。多重箭头函数其实就是函数珂里化的语法糖(fn(a,b,c)改造成fn(a)(b)(c)),让我们完成一个参数对齐

const apply = f => a => f(a);const flip = f => b => a => f(a) (b);const uncurry = f => (a, b) => f(a) (b);const push = x => xs => (xs.push(x), xs);const fold = f => acc => xs => xs.reduce(uncurry(f), acc);const some = f => xs => xs.some(apply(f));const dealArrByFn = f => fold(acc => x => some(f(x)) (acc)? acc: push(x) (acc)) ([]);const eq = y => x => x === y;
dealArrByFn(eq)(names)

总结:


http://www.hkcw.cn/article/mpnJfZSWJa.shtml

相关文章

工厂模式 vs 策略模式:设计模式中的 “创建者” 与 “决策者”

在日常工作里&#xff0c;需求变动或者新增功能是再常见不过的事情了。而面对这种情况时&#xff0c;那些耦合度较高的代码就会给我们带来不少麻烦&#xff0c;因为在这样的代码基础上添加新需求往往困难重重。为了保证系统的稳定性&#xff0c;我们在添加新需求时&#xff0c;…

Emacs 折腾日记(二十六)——buffer与窗口管理

本节我们将介绍如何在Emacs中的buffer与窗口管理&#xff0c;目标是快速管理窗口&#xff0c;以及快速在不同buffer中进行切换 基本概念介绍 Emacs与vim相比的一个特点是&#xff0c;Emacs是一个窗口程序&#xff0c;或者说是一个gui程序。而vim是一个终端字符界面程序(当然E…

强化学习(十三)DQN

传统的强化学习算法会使用表格的形式存储状态价值函数 V ( s ) V(s) V(s) 或动作价值函数 Q ( s ) Q(s) Q(s) &#xff0c;但是这样的方法存在很大的局限性。例如&#xff0c;现实中的强化学习任务所面临的状态空间往往是连续的&#xff0c;存在无穷多个状态&#xff0c;在这…

RapidOCR集成PP-OCRv5_det mobile模型记录

该文章主要摘取记录RapidOCR集成PP-OCRv5_mobile_det记录&#xff0c;涉及模型转换&#xff0c;模型精度测试等步骤。原文请前往官方博客&#xff1a; https://rapidai.github.io/RapidOCRDocs/main/blog/2025/05/26/rapidocr%E9%9B%86%E6%88%90pp-ocrv5_det%E6%A8%A1%E5%9E%8B…

【深度学习】13. 图神经网络GCN,Spatial Approach, Spectral Approach

图神经网络 图结构 vs 网格结构 传统的深度学习&#xff08;如 CNN 和 RNN&#xff09;在处理网格结构数据&#xff08;如图像、语音、文本&#xff09;时表现良好&#xff0c;因为这些数据具有固定的空间结构。然而&#xff0c;真实世界中的很多数据并不遵循网格结构&#x…

从“无差别降噪”到“精准语音保留”:非因果优化技术为助听设备和耳机降噪注入新活力

在复杂环境中保持清晰语音感知一直是助听设备与消费级耳机的核心挑战。传统主动降噪&#xff08;ANC&#xff09;技术虽能抑制环境噪声&#xff0c;但会无差别削弱所有声音&#xff0c;导致用户难以听清目标方向的语音&#xff08;如对话者&#xff09;。近年来&#xff0c;开放…

家庭路由器改装,搭建openwrt旁路由以及手机存储服务器,实现外网节点转发、内网穿透、远程存储、接入满血DeepSeek方案

大家好&#xff0c;也是好久没有发文了&#xff0c;最近在捣鼓一些比较有趣的东西&#xff0c;打算跟大家分享一下&#xff01; 先聊一下我的大致方案嘛&#xff0c;最近感觉家里路由器平时一直就只有无线广播供网的功能&#xff0c;感觉这么好的一下嵌入式设备产品不应该就干这…

【Linux】shell脚本的变量与运算

目录 一.变量 1.1什么是变量 1.2变量的命名 1.3变量的调用 1.4字符的转义 1.5变量的取消 二.变量的类型 2.1函数级变量 2.2环境级变量 2.3用户级变量 2.4系统级变量 2.5常见的系统变量 三..特殊变量及定义 3.1用命令的执行结果定义变量 3.2传参变量 3.3交互式传…

Linux进程概念

一.冯诺依曼体系结构 冯诺依曼体系结构是当代计算机的基本结构&#xff0c;它主要包括几个板块&#xff0c;输入设备&#xff0c;输出设备&#xff0c;存储器&#xff0c;运算器和控制器。 下面是简略版的图解析&#xff1a; 输入设备主要包含鼠标&#xff0c;键盘&#xff0…

[9-2] USART串口外设 江协科技学习笔记(9个知识点)

1 2 3 智能卡、IrDA和LIN是三种不同的通信技术&#xff0c;它们在电子和汽车领域中有着广泛的应用&#xff1a; • 智能卡&#xff08;Smart Card&#xff09;&#xff1a; • 是什么&#xff1a;智能卡是一种带有嵌入式微处理器和存储器的塑料卡片&#xff0c;可以存储和处理数…

低代码——表单生成器以form-generator为例

主要执行流程说明&#xff1a; 初始化阶段 &#xff1a; 接收表单配置对象formConf深拷贝配置&#xff0c;初始化表单数据和验证规则处理每个表单组件的默认值和特殊配置&#xff08;如文件上传&#xff09; 渲染阶段 &#xff1a; 通过render函数创建el-form根组件递归渲染表…

奥威BI+AI——高效智能数据分析工具,引领数据分析新时代

随着数据量的激增&#xff0c;企业对高效、智能的数据分析工具——奥威BIAI的需求日益迫切。奥威BIAI&#xff0c;作为一款颠覆性的数据分析工具&#xff0c;凭借其独特功能&#xff0c;正在引领数据分析领域的新纪元。 一、‌零报表环境下的极致体验‌ 奥威BIAI突破传统报表限…

grid网格布局

使用flex布局的痛点 如果使用justify-content: space-between;让子元素两端对齐&#xff0c;自动分配中间间距&#xff0c;假设一行4个&#xff0c;如果每一行都是4的倍数那没任何问题&#xff0c;但如果最后一行是2、3个的时候就会出现下面的状况&#xff1a; /* flex布局 两…

基于 GitLab CI + Inno Setup 实现 Windows 程序自动化打包发布方案

在 Windows 桌面应用开发中&#xff0c;实现自动化构建与打包发布是一项非常实用的工程实践。本文以我在开发PackTes项目时的为例&#xff0c;介绍如何通过 GitLab CI 配合 Inno Setup、批处理脚本、Qt 构建工具&#xff0c;实现版本化打包并发布到共享目录的完整流程。 项目地…

江西某石灰石矿边坡自动化监测

1. 项目简介 该矿为露天矿山&#xff0c;开采矿种为水泥用石灰岩&#xff0c;许可生产规模200万t/a&#xff0c;矿区面积为1.2264km2&#xff0c;许可开采深度为422m&#xff5e;250m。矿区地形为东西一北东东向带状分布&#xff0c;北高南低&#xff0c;北部为由浅变质岩系组…

星海掘金:校园极客的Token诗篇(蓝耘MaaS平台)——从数据尘埃到智能生命的炼金秘录

hi&#xff0c;我是云边有个稻草人 目录 前言 一、初识蓝耘元生代MaaS平台&#xff1a;零门槛体验AI服务 1.1 从零开始——平台注册与环境搭建 1.2 平台核心功能 1.3 蓝耘平台的优势在哪里&#xff1f; 二、知识库构建新篇章&#xff1a;从零碎资料到智能语义仓库的蜕变…

测试概念 和 bug

一 敏捷模型 在面对在开发项目时会遇到客户变更需求以及合并新的需求带来的高成本和时间 出现的敏捷模型 敏捷宣言 个人与交互重于过程与工具 强调有效的沟通 可用的软件重于完备的文档 强调轻文档重产出 客户协作重于合同谈判 主动及时了解当下的要求 相应变化…

14. 最长公共前缀

以示例1为例 从左到右依次比较每个字符&#xff1a; 第一个字符都是f&#xff0c;当前最长公共前缀为"f"第二个字符都是l&#xff0c;当前最长公共前缀更新为"fl"第三个字符不一致&#xff0c;因此最终最长公共前缀确定为"fl" 具体实现思路&am…

【CF】Day69——⭐Codeforces Round 897 (Div. 2) D (图论 | 思维 | DFS | 环)

D. Cyclic Operations 题目&#xff1a; 思路&#xff1a; 非常好的一题 对于这题我们要学会转换和提取条件&#xff0c;从特殊到一般 我们可以考虑特殊情况先&#xff0c;即 k n 和 k 1时&#xff0c;对于 k 1&#xff0c;我们可以显然发现必须满足 b[i] i&#xff0c;而…

[9-1] USART串口协议 江协科技学习笔记(13个知识点)

1 2 3 4全双工就是两个数据线&#xff0c;半双工就是一个数据线 5 6 7 8 9 10 TTL&#xff08;Transistor-Transistor Logic&#xff09;电平是一种数字电路中常用的电平标准&#xff0c;它使用晶体管来表示逻辑状态。TTL电平通常指的是5V逻辑电平&#xff0c;其中&#xff1a;…