【JavaScript】获取多项数组的交集

题外话:
  我本来打算百度的,毕竟自己写太烦。但是找到的好多代码,都会出现各种问题。

比如:
  数组A【0,0,0,0,1,1,1,1,1】,数组B【2,2,‘2’,4,4,4】,求它们的交集,会返回【0,1,2,4】。或者,不支持字符串等等。

原因:
  没有去重,0 被存储在哈希对象中,被显示出现 4 次。又因为,是通过判断元素在哈希对象中出现次数是否等于 arguments.length 来进行判断它是否为交集元素的。所以一旦数组出现重复项或者稍微复杂一点的数据(排除引用类型数据),就会出现很多瑕疵,不够严谨。所以我才动手改良了下。
  去重后,每个元素只会在当前数组中出现一次,那么就可以通过标记该元素是否出现的次数等于数组数量,来进行判断是否为交集元素,这样就不会出现因为重复项导致结果错误的现象。
  性能可能没那么好,但起码准确性较高。

在这里插入图片描述


注意:
1. 不支持引用类型数据(存在引用类型数据的数组应该不需要求交集吧);
2. 不同数据类型但同值的数据,会识别为相同的元素,类似相等运算符;
3. 在转换过程中,阻止了 null 转为 0,true 转为 1,false 转为 0;
4. NaN 与 'NaN' 或者它本身,都进行了处理,使它们能够“相等”;
5. 关于返回的交集数组的元素数据类型:除非只能为字符串,否则都不会以字符串类型返回

关于 NaN 的处理:
在这里插入图片描述


思路:普通排序、数字升序、去重、存储相同元素

/**
 * 含 forEach()、map() 不被低版本 IE 支持
 * 
 * [intersect 用于获取数组间的交集]
 * @return {[array]} [返回交集数组]
 *
 * 注意:
 * 		1. undefined、'undefined' 等(字符串与原值)不会被区分,归为相同值;
 * 		2. NaN 与 NaN、'NaN' 也进行了处理,使之能够被去重、求交集;
 * 		3. 关于字符串 'undefined' 与 undefined 数据为何相等,因为 value in object 不区分该"属性名"是否带有引号;
 * 		4. 所以 1、'1' 也是相同的,虽然一个是 string,一个是 number,但它们数值相同;
 * 		5. 除了特殊值的字符串形式,其余字符串都原样返回;
 * 		6. 由于使用浅拷贝,且无法(或困难)将引用类型数据进行去重处理(object就特烦),所以不支持含引用类型元素的数组;
 */
Array.intersect = function() {
	var hash = {},	// 哈希对象
		result = [],	// 返回的结果
		arr = [];	// 去重后的数组

	/**
	 * 去重,避免因数组内的重复项而影响结果【对原数据类型与字符串不进行区分,如:undefined 与 'undefined' 相同】
	 * 
	 * @param  {[type]} arg [由外层传递进来的 arguments 参数]
	 * @return {[none]}     [不返回值,虽然对数组进行去重操作,但不会影响原数组(浅拷贝)]
	 */
	;(function(arg) {
		for (var i = 0; i < arg.length; i++) {
			for (var j = 0; j < arg[i].length; j++) {
				var n = 1,	// 第一项不变动
					count = 0;	// 从 0 开始计数

				// 浅拷贝【避免影响原数据】,先正常排序(针对字符串),再升序排序(针对数字)
				arr[i] = arg[i].map(function(ele, idnex, arr) {
					return ele;
				}).sort().sort(function(prev, cur) {
					return prev - cur;
				});

				// 去重,ele:当前元素、index:当前下标、arr:调用 forEach() 的当前数组
				arr[i].forEach(function(ele, index, arr) {
					if (index > 0) {
						// 转为字符串再进行判断,将 string 1 与 number 1 视为相等元素
						if (arr[index]+'' !== arr[index-1]+'') {
							arr[n++] = arr[index];
						} else {
							count++;
						}
					}
				});
				arr[i].length -= count;	// 移除重复项后,需要减去对应长度
			}
		}
	})(arguments);

	// console.log(arr);	// 将参数数组,去重后并排序后,返回的结果

	// 获取去重数据后,再进行交集处理
	for (var i = 0; i < arr.length; i++) {
		for (var j = 0; j < arr[i].length; j++) {
			var item = arr[i][j];

			if (!hash[item]) {	// 在哈希对象找不到该项,则添加,并初始计数 1
				hash[item] = 1;
			} else {	// 若能找到,则递增计数
				hash[item]++;
				if (hash[item] === arguments.length) {	// 去重后,若该项计数次数等于 数组数量 ,即表示该项为交集元素
					/** 
					 * 1. 特殊值以原数据类型返回,如:'undefined' 会以 undefined 返回;
					 * 2. string 数字均会以 number 返回;
					 * 3. 其余 string 还是以 string 返回;
					 * 4. 如此一来,返回的交集数组的元素,能不以字符串显示就不以字符串显示;
					 * 5. 不必纠结 'undefined' 是否字为符串,把它归为 undefined 就好了;
					 * 6. 同数组的 NaN 或 'NaN' 也能被去重,并且不同数组的 NaN 或 'NaN' 也能纳入交集数组
					 */
					// 用于匹配特殊值
					var r_obj = {
						'true': '用',
						'false': '于',
						'null': '匹',
						'undefined': '配',
						'NaN': '。'
					};
					if (item in r_obj) {	// 如此,'undefined' 与 undefined 便会被识别为相同值
						if (item === true || item === 'true') {
							result.push(true);
						} else if (item === false || item === 'false') {
							result.push(false);
						} else if (item === null || item === 'null') {
							result.push(null);
						} else if (item === undefined || item === 'undefined') {
							result.push(undefined);
						} else if (item+'' === 'NaN') {
							result.push(NaN);
						}
					} else if (!isNaN(item)) {	// 数字都会以 number 显示
						result.push(item/1);
					} else if (isNaN(item)) {	// 其余字符串则不变
						result.push(item);
					};
				};
			}
		}
	}

	console.log(hash);	// 返回哈希对象,查看去重后各项元素出现的次数
			
	// 输出升序结果数组
	return result.sort(function(a, b) {
		return a - b;
	});
};

兼容:for 模仿 forEach()、concat() 替代 map()

/**
 * 使用 for 模仿 forEach(),concat() 替代 map(),兼容低版本 IE
 */
Array.intersect = function() {
	var hash = {},	// 哈希对象
		result = [],	// 返回的结果
		arr = [];	// 去重后的数组

	// 去重
	;(function(arg) {
		for (var i = 0; i < arg.length; i++) {
			for (var j = 0; j < arg[i].length; j++) {

				// 浅拷贝【避免影响原数据】
				arr[i] = arg[i].concat([]);
				// 先正常排序(针对字符串),再升序排序(针对数字)
				arr[i].sort().sort(function(prev, cur) {
					return prev - cur;
				});

				/**
				 * [for 去重处理,模仿 forEach,用于兼容]
				 * @param  {Number} k     [遍历参数数组每项元素]
				 * @param  {Number} n     [第一项不变动,然后遇到相邻重复项都会跳过,重新定义数组]
				 * @param  {[type]} count [计数器,计算重复项目的数量,最后以原数组长度减去该数量,得到去重后的数组的长度]
				 * @return {[none]}       [去重操作,无返回]
				 */
				for (var k = 0, n = 1, count = 0; k < arr[i].length; k++) {
					if (k > 0) {
						if (arr[i][k]+'' !== arr[i][k-1]+'') {
							arr[i][n++] = arr[i][k]
						} else {
							count++;
						}
					}
				}

				arr[i].length -= count;
			}
		}
	})(arguments);

	// console.log(arr);	// 将参数数组,去重后并排序后,返回的结果

	// 获取去重数据后,再进行交集处理
	for (var i = 0; i < arr.length; i++) {
		for (var j = 0; j < arr[i].length; j++) {
			var item = arr[i][j];

			if (!hash[item]) {	// 在哈希对象找不到该项,则添加,并初始计数 1
				hash[item] = 1;
			} else {	// 若能找到,则递增计数
				hash[item]++;
				if (hash[item] === arguments.length) {	// 去重后,若该项计数次数等于 数组数量 ,即表示该项为交集元素
					/** 
					 * 1. 特殊值以原数据类型返回,如:'undefined' 会以 undefined 返回;
					 * 2. string 数字均会以 number 返回;
					 * 3. 其余 string 还是以 string 返回;
					 * 4. 如此一来,返回的交集数组的元素,能不以字符串显示就不以字符串显示;
					 * 5. 不必纠结 'undefined' 是否字为符串,把它归为 undefined 就好了;
					 * 6. 同数组的 NaN 或 'NaN' 也能被去重,并且不同数组的 NaN 或 'NaN' 也能纳入交集数组
					 */
					// 用于匹配特殊值
					var r_obj = {
						'true': '用',
						'false': '于',
						'null': '匹',
						'undefined': '配',
						'NaN': '。'
					};
					if (item in r_obj) {	// 如此,'undefined' 与 undefined 便会被识别为相同值
						if (item === true || item === 'true') {
							result.push(true);
						} else if (item === false || item === 'false') {
							result.push(false);
						} else if (item === null || item === 'null') {
							result.push(null);
						} else if (item === undefined || item === 'undefined') {
							result.push(undefined);
						} else if (item+'' === 'NaN') {
							result.push(NaN);
						}
					} else if (!isNaN(item)) {	// 数字都会以 number 显示
						result.push(item/1);
					} else if (isNaN(item)) {	// 其余字符串则不变
						result.push(item);
					};
				};
			}
		}
	}

	console.log(hash);	// 返回哈希对象,查看各项元素出现的次数
			
	// 输出升序结果数组
	return result.sort(function(a, b) {
		return a - b;
	});
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值