搜索
首页web前端js教程JavaScript的数组去重方法

JavaScript的数组去重方法

Nov 27, 2017 am 09:24 AM
javascriptjs数组

我们都知道不同编程语言都有数组去重,本篇文章我们就讲述JavaScript数组去重,希望能帮助到大家。

双层循环

也许我们首先想到的是使用 indexOf 来循环判断一遍,但在这个方法之前,让我们先看看最原始的方法:

var array = [1, 1, '1', '1'];function unique(array) {    // res用来存储结果
    var res = [];    for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {        for (var j = 0, resLen = res.length; j < resLen; j++ ) {            if (array[i] === res[j]) {                break;
            }
        }        // 如果array[i]是唯一的,那么执行完循环,j等于resLen
        if (j === resLen) {
            res.push(array[i])
        }
    }    return res;
}console.log(unique(array)); // [1, "1"]

在这个方法中,我们使用循环嵌套,最外层循环 array,里面循环 res,如果 array[i] 的值跟 res[j] 的值相等,就跳出循环,如果都不等于,说明元素是唯一的,这时候 j 的值就会等于 res 的长度,根据这个特点进行判断,将值添加进 res。

看起来很简单吧,之所以要讲一讲这个方法,是因为——————兼容性好!

indexOf

我们可以用 indexOf 简化内层的循环:

var array = [1, 1, &#39;1&#39;];function unique(array) {    var res = [];    for (var i = 0, len = array.length; i < len; i++) {        var current = array[i];        if (res.indexOf(current) === -1) {
            res.push(current)
        }
    }    return res;
}console.log(unique(array));

排序后去重

试想我们先将要去重的数组使用 sort 方法排序后,相同的值就会被排在一起,然后我们就可以只判断当前元素与上一个元素是否相同,相同就说明重复,不相同就添加进 res,让我们写个 demo:

var array = [1, 1, &#39;1&#39;];function unique(array) {    var res = [];    var sortedArray = array.concat().sort();    var seen;    for (var i = 0, len = sortedArray.length; i < len; i++) {        // 如果是第一个元素或者相邻的元素不相同
        if (!i || seen !== sortedArray[i]) {
            res.push(sortedArray[i])
        }
        seen = sortedArray[i];
    }    return res;
}console.log(unique(array));

如果我们对一个已经排好序的数组去重,这种方法效率肯定高于使用 indexOf。

unique API

知道了这两种方法后,我们可以去尝试写一个名为 unique 的工具函数,我们根据一个参数 isSorted 判断传入的数组是否是重复的,如果为 true,我们就判断相邻元素是否相同,如果为 false,我们就使用 indexOf 进行判断

var array1 = [1, 2, &#39;1&#39;, 2, 1];var array2 = [1, 1, &#39;1&#39;, 2, 2];// 第一版function unique(array, isSorted) {    var res = [];    var seen = [];    for (var i = 0, len = array.length; i < len; i++) {        var value = array[i];        if (isSorted) {            if (!i || seen !== value) {
                res.push(value)
            }
            seen = value;
        }        else if (res.indexOf(value) === -1) {
            res.push(value);
        }        
    }    return res;
}console.log(unique(array1)); // [1, 2, "1"]console.log(unique(array2, true)); // [1, "1", 2]

优化

尽管 unqique 已经可以试下去重功能,但是为了让这个 API 更加强大,我们来考虑一个需求:

新需求:字母的大小写视为一致,比如'a'和'A',保留一个就可以了!

虽然我们可以先处理数组中的所有数据,比如将所有的字母转成小写,然后再传入unique函数,但是有没有方法可以省掉处理数组的这一遍循环,直接就在去重的循环中做呢?让我们去完成这个需求:

var array3 = [1, 1, &#39;a&#39;, &#39;A&#39;, 2, 2];// 第二版// iteratee 英文释义:迭代 重复function unique(array, isSorted, iteratee) {    var res = [];    var seen = [];    for (var i = 0, len = array.length; i < len; i++) {        var value = array[i];        var computed = iteratee ? iteratee(value, i, array) : value;        if (isSorted) {            if (!i || seen !== value) {
                res.push(value)
            }
            seen = value;
        }        else if (iteratee) {            if (seen.indexOf(computed) === -1) {
                seen.push(computed);
                res.push(value);
            }
        }        else if (res.indexOf(value) === -1) {
            res.push(value);
        }        
    }    return res;
}console.log(unique(array3, false, function(item){    return typeof item == &#39;string&#39; ? item.toLowerCase() : item
})); // [1, "a", 2]

在这一版也是最后一版的实现中,函数传递三个参数:

array:表示要去重的数组,必填

isSorted:表示函数传入的数组是否已排过序,如果为 true,将会采用更快的方法进行去重

iteratee:传入一个函数,可以对每个元素进行重新的计算,然后根据处理的结果进行去重

至此,我们已经仿照着 underscore 的思路写了一个 unique 函数,具体可以查看 Github。

filter

ES5 提供了 filter 方法,我们可以用来简化外层循环:

比如使用 indexOf 的方法:

var array = [1, 2, 1, 1, &#39;1&#39;];function unique(array) {    var res = array.filter(function(item, index, array){        return array.indexOf(item) === index;
    })    return res;
}console.log(unique(array));

排序去重的方法:

var array = [1, 2, 1, 1, &#39;1&#39;];function unique(array) {    return array.concat().sort().filter(function(item, index, array){        return !index || item !== array[index - 1]
    })
}console.log(unique(array));

Object 键值对

去重的方法众多,尽管我们已经跟着 underscore 写了一个 unqiue API,但是让我们看看其他的方法拓展下视野:

这种方法是利用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。示例代码如下:

var array = [1, 2, 1, 1, &#39;1&#39;];function unique(array) {    var obj = {};    return array.filter(function(item, index, array){        return obj.hasOwnProperty(item) ? false : (obj[item] = true)
    })
}console.log(unique(array)); // [1, 2]

我们可以发现,是有问题的,因为 1 和 '1' 是不同的,但是这种方法会判断为同一个值,这是因为对象的键值只能是字符串,所以我们可以使用 typeof item + item 拼成字符串作为 key 值来避免这个问题:

var array = [1, 2, 1, 1, &#39;1&#39;];function unique(array) {    var obj = {};    return array.filter(function(item, index, array)
{        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}console.log(unique(array)); // [1, 2, "1"]

ES6

随着 ES6 的到来,去重的方法又有了进展,比如我们可以使用 Set 和 Map 数据结构,以 Set 为例,ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

是不是感觉就像是为去重而准备的?让我们来写一版:

var array = [1, 2, 1, 1, &#39;1&#39;];function unique(array) {   return Array.from(new Set(array));
}console.log(unique(array)); // [1, 2, "1"]

甚至可以再简化下:

function unique(array) {    return [...new Set(array)];
}

还可以再简化下:

var unique = (a) => [...new Set(a)]

此外,如果用 Map 的话:

function unique (arr) {    const seen = new Map()    return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}

JavaScript 的进化

我们可以看到,去重方法从原始的 14 行代码到 ES6 的 1 行代码,其实也说明了 JavaScript 这门语言在不停的进步,相信以后的开发也会越来越高效。

特殊类型比较

去重的方法就到此结束了,然而要去重的元素类型可能是多种多样,除了例子中简单的 1 和 '1' 之外,其实还有 null、undefined、NaN、对象等,那么对于这些元素,之前的这些方法的去重结果又是怎样呢?

在此之前,先让我们先看几个例子:

var str1 = &#39;1&#39;;var str2 = new String(&#39;1&#39;);console.log(str1 == str2);
 // trueconsole.log(str1 === str2); // falseconsole.log(null == null);
  // trueconsole.log(null === null); // trueconsole.log(undefined == undefined);
   // trueconsole.log(undefined === undefined); // trueconsole.log(NaN == NaN);
    // falseconsole.log(NaN === NaN); // falseconsole.log(/a/ == /a/); 
    // falseconsole.log(/a/ === /a/); // falseconsole.log({} == {});
     // falseconsole.log({} === {});
      // false

那么,对于这样一个数组

var array = [1, 1, &#39;1&#39;, &#39;1&#39;, null, null, undefined, undefined, new String(&#39;1&#39;), new String(&#39;1&#39;), /a/, /a/, NaN, NaN];

以上各种方法去重的结果到底是什么样的呢?

我特地整理了一个列表,我们重点关注下对象和 NaN 的去重情况:


方法      结果      说明


for循环    [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN]    对象和 NaN 不去重    

indexOf    [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN]    对象和 NaN 不去重    

sort    [/a/, /a/, "1", 1, String, 1, String, NaN, NaN, null, undefined]    对象和 NaN 不去重 数字 1 也不去重    

filter + indexOf    [1, "1", null, undefined, String, String, /a/, /a/]    对象不去重 NaN 会被忽略掉    

filter + sort    [/a/, /a/, "1", 1, String, 1, String, NaN, NaN, null, undefined]    对象和 NaN 不去重 数字 1 不去重    

优化后的键值对方法    [1, "1", null, undefined, String, /a/, NaN]    全部去重    

Set    [1, "1", null, undefined, String, String, /a/, /a/, NaN]    对象不去重 NaN 去重    

想了解为什么会出现以上的结果,看两个 demo 便能明白:

// demo1var arr = [1, 2, NaN];
arr.indexOf(NaN); // -1

indexOf 底层还是使用 === 进行判断,因为 NaN ==== NaN的结果为 false,所以使用 indexOf 查找不到 NaN 元素

// demo2function unique(array) {   return Array.from(new Set(array));
}console.log(unique([NaN, NaN])) // [NaN]

Set 认为尽管 NaN === NaN 为 false,但是这两个元素是重复的。

写在最后

虽然去重的结果有所不同,但更重要的是让我们知道在合适的场景要选择合适的方法。

以上内容就是各种不同的JavaScript数组去重方法,如果大家觉得有用的话赶紧收藏起来吧。

相关推荐:

javascript数组去重/查找/插入/删除的方法

js数组去重方法汇总

JavaScript数组去重方法总结

JS实现数组去重的实例介绍

js实现数组去重的几种方法总结

以上是JavaScript的数组去重方法的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Python vs. JavaScript:开发环境和工具Python vs. JavaScript:开发环境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

JavaScript是用C编写的吗?检查证据JavaScript是用C编写的吗?检查证据Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。

JavaScript的角色:使网络交互和动态JavaScript的角色:使网络交互和动态Apr 24, 2025 am 12:12 AM

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C和JavaScript:连接解释C和JavaScript:连接解释Apr 23, 2025 am 12:07 AM

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

从网站到应用程序:JavaScript的不同应用从网站到应用程序:JavaScript的不同应用Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python vs. JavaScript:比较用例和应用程序Python vs. JavaScript:比较用例和应用程序Apr 21, 2025 am 12:01 AM

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C/C在JavaScript口译员和编译器中的作用C/C在JavaScript口译员和编译器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在行动中:现实世界中的示例和项目JavaScript在行动中:现实世界中的示例和项目Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具