前言

  Js数组去重已经有很多种实现方式:包括逐个检索对比(使用Array.property.indexOf),先排序后对比,使用hash表,利用ES6中的Set()等。这些数组去重办法中速度最快的是hash表,最安全也最慢的是逐个检索对比(先排序后对比是优化成先分组再逐个检索),而ES6的Set对象目前浏览器兼容不全。

  有没有结合那些以上方式的优点,像hash表一样快,和Array.property.indexOf一样全,又没有兼容问题的解决方案呢?

  有!

Js中的基本类型

  Undefined, Number, String,Boolean, Null, Object,除了前5种是值传递类型,其他所有的都是引用类型。

待解决问题

  假设有o1 = o2 = new Object()

  hash表去重方式的缺陷在于不能判断[o1,o2,{}]中的o1与o2是同一个对象的引用,因为得到的结果都是 ”object”。

那么如果有办法判断引用变量后面是同一个对象,就可以达到Array.property.indexOf方式所做的效果了。但是js本身又不提供对象内存地址索引,所以,那就造一个吧。

标记法

  比如有个人类,在学校老师叫他做“小明”,在家爸妈叫他做“乖乖”,邻居叫他做“别人家的孩子”,虽然叫法不同,但是实际都是指向同一个人类个体。有一次商家搞活动给小孩子发免费糖果,一个小屁孩只能领一次,为了避免熊孩子以“小明”的名义领一次,又以“乖乖”的名义领一次,商家想了一个办法,每一个来领糖果的小孩子,先检查手掌有没有商家画的logo,如果没有,就可以领糖果,发完糖果后就在小孩子手掌上画商家logo,下次再来时只要看手掌上是不是已经有logo了,有就说明已经领过了,就不发糖果。同时为了不让logo与小孩子身上其他涂涂画画的东西相同,商家特意找了一种叫“全宇宙重复概率可能性低到接近不存在”的符号当logo,成功解决了涂鸦与logo相似的问题。小孩子领了糖果也吃完了,怕回家被爸妈发现,就去洗了手上的logo,就像一切都没有发生过一样。

所以呢,只要用一个uuid(商家logo)作为属性值添加到对象里面(画到手掌上),就可以判断这个对象在前面已经被收录(领过糖果)了,也就达到检测多个引用变量是否指向同一个对象的效果了。引用类型在去重之后,还要将uuid属性清除掉,避免后续该对象的使用出现问题。

  使用uuid做标记key名,目的是避免与对象上本来就存在的属性名冲突,比如原对象有个minName属性名,标记名就不能叫minName,所以选uuid做标记属性名是为了与该对象当前所有可能存在的属性名区分,而每一个对象都是独立的,那么只需要一个uuid字符串就可以了。

  

具体代码实现与数据测试

 !(function() {
"user strict"; /**
* 创建一个 带有默认格式的uuid字符串
* @param {Number} length 4个字符串为一组的组数数量 default:8
* @param {String} connectString 组数之间连接符 default:"_"
* @return uuidString:7953_1e59_9cc4_8f44_ab7d_c959_3b5c_ef94
* @author Z
*/ function UUID(length, connectString) {
var uuid = [],
max0x = 0x10000, //65535+1
i; // 设置参数num,connectString默认值
length = Math.abs(0 | length) || 8;
typeof connectString !== "string" && (connectString = "_"); for (i = 0; i < length; i++) {
uuid.push((Math.random() * max0x | 0).toString(16));
} return uuid.join(connectString);
}
// demo
// UUID() ===> eae1_bb78_1f00_4ef2_c5d0_d28b_6708_e73f
// UUID(null,"-") ===> ff15-8779-ce3d-3c83-cab3-b645-9150-cdd2
// UUID(4) ===> d9d9_7a23_4ce7_c5c0
// UUID(4,"&") ===> 8703&6daf&8daa&f36d
/** ----test UUID()--- */
// console.log(UUID());
// console.log(UUID(null, "-"));
// console.log(UUID(4));
// console.log(UUID(4, "&")); /**
* 用于拓展操作Array对象的工具集合
* @type {Object}
*/
var _arrayExpand = {}; /**
* 将传入的数组去掉重复的项,并返回一个新数组。
* @param {Array} array 目标去重数组,原始数组
* @param {Boolean} isQuick 可选参数, 等于true时使用判断步骤更少的hash去重
* @return {Array} 去重后的新数组
* @author Z
*/
_arrayExpand.unique = function(array, isQuick) {
var temp = [],
valueHash = {
"number": {},
"string": {},
"boolean": {},
"undefined": {},
"null": true // 只需要判断一次即可
},
objectHash = {},
objectRecord = [],
quickHash = {},
keyUUID, item, type, i, len, _set // 优先使用 ES6 中的 Set对象
if (window.Set) {
_set = new Set(array);
for (i of _set) {
if (_set.has(i)) {
temp.push(i);
}
}
return temp;
} // 已知所有项都是值传递类型的数组,可以指定使用快速去重,isQuick指定为true值时生效
if (isQuick === true) {
for (i = 0, len = array.length; i < len; i++) {
item = array[i];
if (!quickHash[item]) {
temp.push(item);
quickHash[item] = true;
}
}
}
// 包含引用类型的混合数组去重
else {
// 标记属性使用uuid,避免与其他属性冲突, 使用上面的UUID创建,或者直接手写。
keyUUID = UUID(); for (i = 0, len = array.length; i < len; i++) {
item = array[i];
type = valueHash[typeof item];
// number, string, boolean, undefined
if (type) {
if (!type[item]) {
temp.push(item);
type[item] = true;
}
}
// object, null
else {
// object
if (item) {
if (!item.hasOwnProperty(keyUUID)) {
temp.push(item);
item[keyUUID] = true;
// 标记污染了原先的object,后续需要清除标记
objectRecord.push(item);
}
}
// null
else if (valueHash.null) {
temp.push(item);
valueHash.null = false;
}
}
} // 清除标记
if (objectRecord.length) {
for (i = 0, len = objectRecord.length; i < len; i++) {
delete objectRecord[i][keyUUID];
}
} } return temp;
};
// demo
// _arrayExpand.unique([1,"1",{o1},{o1},{m1},{m2}, new Number(1), null,undefined,NaN,Infinity,null,undefined,NaN,Infinity]) ===> [1,"1",{o1},{m1},{m2}, new Number(1), null,undefined,NaN,Infinity]
// _arrayExpand.unique([1,2,3,4,1,2,3,4], true) ===> [1,2,3,4]
/** -----test _arrayExpand.unique()--- */
// var o1 = {},
// o2 = o1,
// o3 = o1;
// console.log(_arrayExpand.unique([new Number(1), 1, 1, {}, "1", {}, true, null, "true", null, undefined, 0, NaN, NaN, o2, Infinity, o1, o3])); // 添加到window,对外调用
var _ArrayExpandName = "ArrayExpand";
if (window.hasOwnProperty(_ArrayExpandName)) {
alert(_ArrayExpandName + " was existed in the window");
} else {
window[_ArrayExpandName] = _arrayExpand;
}
// openApi demo
// ArrayExpand[key](arguments)
// window.ArrayExpand[key](arguments) }(undefined));

但是,但是,总是又有但是,更加全面的去重是先检测当对象已经被阻止扩展或者冻结,如Object.preventExtensions,Object.seal,Object.freeze之类,就不能使用标记法了,只能上用ES6的Set对象,或者降级使用Array.property.indexOf。

end! 谨以此文写给[yoyohao]

Bruce-CZ原创

js引用类型数组去重-对象标记法的更多相关文章

  1. JS实现数组去重方法大总结

    js数组根据对象中的元素去重: var arr2 = [ { name: "name1", num: "1" }, { name: "name2&qu ...

  2. Js对于数组去重提高效率一些心得

    最近在找工作,好几次面试都问过数组去重的问题.虽然问的都不一样,但是核心思想是没有变的. 第一种是比较常规的方法 思路: 构建一个新的数组存放结果 for循环中每次从原数组中取出一个元素,用这个元素循 ...

  3. js中数组去重的几种方法

    js中数组去重的几种方法         1.遍历数组,一一比较,比较到相同的就删除后面的                 function unique(arr){                 ...

  4. JS实现数组去重方法整理

    前言 我们先来看下面的例子,当然来源与网络,地址<删除数组中多个不连续的数组元素的正确姿势> 我们现在将数组中所有的‘ a’ 元素删除: var arr = ['a', 'a', 'b', ...

  5. js06--利用js给数组去重

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  6. js对数组去重的方法总结-(2019-1)

    最近待业在家,系统地学习了一套js的课程.虽然工作时间真的比较长了,但有些东西只局限在知其然而不知其所以然的程度上,有些知识点通过“血和泪”的经验积累下来,也只是记了结果并没有深究,所以每次听完课都有 ...

  7. js实现数组去重的方式(7种)

    JS数组去重的方式 例:将下面数组去除重复元素(以多种数据类型为例) const arr = [1, 2, 2, 'abc', 'abc', true, true, false, false, und ...

  8. js中数组去重的方法

    在实际工作或面试中,我们经常会遇到"数组去重"问题,接下来就是使用js实现的数组去重的多种方法: 1.借助ES6提供的Set结构 var arr = [1,1,2,2,3,3,4, ...

  9. js之数组,对象,类数组对象

    许久不写了,实在是不知道写点什么,正好最近有个同事问了个问题,关于数组,对象和类数组的,仔细说起来都是基础,其实都没什么好讲的,不过看到还是有很多朋友有些迷糊,这里就简单对于定义以及一下相同点,不同点 ...

随机推荐

  1. Linux下ls命令显示符号链接权限为777的探索

    Linux下ls命令显示符号链接权限为777的探索 --深入ls.链接.文件系统与权限 一.摘要 ls是Linux和Unix下最常使用的命令之一,主要用来列举目录下的文件信息,-l参数允许查看当前目录 ...

  2. 无向图的完美消除序列 判断弦图 ZOJ 1015 Fish net

       ZOJ1015 题意简述:给定一个无向图,判断是否存在一个长度大于3的环路,且其上没有弦(连接环上不同两点的边且不在环上). 命题等价于该图是否存在完美消除序列. 所谓完美消除序列:在 vi,v ...

  3. ajax跨域问题及解决

    overview ajax是一种创建交互式网页应用的网页开发技术,是一种用于创建快速动态网页的技术,通过在后台与服务器进行少量数据交换.而ajax的跨域问题则是请求了其他项目的接口地址,当协议.子域名 ...

  4. 上传图片,多图上传,预览功能,js原生无依赖

    最近很好奇前端的文件上传功能,因为公司要求做一个支持图片预览的图片上传插件,所以自己搜了很多相关的插件,虽然功能很多,但有些地方不能根据公司的想法去修改,而且需要依赖jQuery或Bootstrap库 ...

  5. web微信开发前期准备最新详细流程

    一.申请配置测试公众号与配置本地服务器   1.打开浏览器,输入:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login,微信扫码确 ...

  6. Django之Model世界

    Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数 ...

  7. 【2-26】string/math/datetime类的定义及其应用

    一string类 (1)字符串.Length    Length作用于求字符串的长度,返回一个int值 (2)字符串.TrimStart();  TrimStart():可删除前空格,返回一个stri ...

  8. (七)javascript中的数组

    一. 一维数组 1.1 声明数组 var 数组名=new Array(数组大小); 1.2  添加元素 <script> var a=new Array(3); a[0]="张三 ...

  9. Single Number leetcode

    Given an array of integers, every element appears twice except for one. Find that single one. Note:Y ...

  10. 1491: [NOI2007]社交网络

    1491: [NOI2007]社交网络 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 881  Solved: 518[Submit][Status] ...