-
Notifications
You must be signed in to change notification settings - Fork 1.4k
symmetric difference
S1ngS1ng edited this page Feb 6, 2018
·
4 revisions
- 这个
function
接收一个参数arg
,其中包含至少两个数组。返回值也为数组,即为给出数组的对称差 - 如果
arg
是[1, 2, 3]
和[5, 1, 2, 4]
,则返回值为[3, 4, 5]
- 建议先去了解一下对称差的定义
- 根据定义,对于两个数组来说,对称差的意思就是:
- 要么在第一个数组中,要么在第二个数组中。换句话说,在第一个数组中排除在第二个数组的元素,然后在第二个数组中排除在第一个数组的元素
- 从另一个角度考虑,其实就是先把两个数组中所有元素都合并成一个数组,然后排除掉既在第一个数组也在第二个数组的元素
- 对于多个数组的操作也不难理解。设
A
、B
和C
分别代表三个数组,设sym()
为求对称差的函数。那么sym(A, B, C)
就相当于sym(sym(A, B), C)
- 需要注意的是,如果数组中有重复元素,那么在结果中是要过滤的。举个例子,如果传入
[1, 1, 2]
和[2, 3]
,那么结果应该是[1, 3]
- 先来看看对于上面提到的两种求对称差的逻辑分别如何实现
function getSym(arr1, arr2) {
// 得到在第一个数组,但不在第二个数组中的元素
var result = arr1.filter(function(e) {
return arr2.indexOf(e) === -1;
});
// 得到在第二个数组,但不在第以个数组中的元素
result.concat(arr2.filter(function(e) {
return arr1.indexOf(e) === -1;
}))
// 去重
return result.filter(function(e, i) {
return result.indexOf(e) === i;
})
}
function getSym(arr1, arr2) {
return arr1.concat(arr2)
.filter(function(e) {
// 排除既在第一个也在第二个数组中的元素
return !(arr1.indexOf(e) > -1 && arr2.indexOf(e) > -1);
})
.filter(function(e, i, arr) {
return arr.indexOf(e) === i;
})
}
- 第一个思路应该不难理解。我们先把在
arr1
中但不在arr2
中的元素直接作为初始值赋给result
。然后,把在arr2
中但不在arr1
中的元素添加到result
里。最后,去重 - 去重的写法可能有点儿不好理解。原理是这样,对于有相同元素的数组,
Array.indexOf
总是返回重复元素中第一个元素的索引。filter
方法的回调函数,第一个参数是元素本身,第二个参数是当前的索引。通过判断这个当前索引和Array.indexOf
返回值是否相等 (相等即保留) 就可以实现去重 - 第二种写法,注意到第二个
filter
方法的回调函数中,我们多设置了一个参数。如果没有很多次链式调用,需要传入第三个参数的情况不是很多,因为我们可以直接通过调用者的变量名去调用 - 注意,这里我们不可以用
this
。原因很简单,因为任何匿名函数的this
指向的都是全局对象,比如window
- 参数是两个数组的情况很好解决,但参数还可能是多个数组。按照一开始的思路,我们要先对前两个求一次
sym
,再对计算结果 (当然,这个计算结果是一个一维数组) 和下一个数组再求一次sym
- 其实这就符合递归的模型,因为我们不确定需要做同样的操作多少次。当然,用循环也是肯定可以解的
- 我们在上面已经封装好了求两数组对称差的代码,只要把这部分应用到题目中就可以了
function sym() {
// 设置初始值
var result = [];
for (var i = 0; i < arguments.length - 1; i++) {
if (result.length > 0) {
// 表示已经调用过 getSym,因此需要传入 result 进行迭代
result = getSym(result, arguments[i + 1]) ;
} else {
// 表示未调用过 getSym,换句话说,这时候就是 i 为 0 的情况
result = getSym(arguments[i], arguments[i + 1]);
}
}
// 按照第二个思路封装的 getSym
function getSym(arr1, arr2) {
return arr1.concat(arr2)
.filter(function(e) {
return !(arr1.indexOf(e) > -1 && arr2.indexOf(e) > -1);
})
.filter(function(e, i, arr) {
return arr.indexOf(e) === i;
});
}
return result;
}
- 需要注意的是,既然我们会在
for
循环中调用i+1
,那么,我们就不能用arguments.length
作为跳出条件了,因为当i
为arguments.length - 1
的时候,i + 1
为arguments.length
。对于一个数组arr
,显然arr[arr.length]
是undefined
- 类似地,如果我们需要在循环中调用
i - 1
,那我们就要把初始值设置为1
,而不是0
。arr[-1]
虽然不会报错,但它会访问数组的最后一个元素,很可能会影响结果
- 其实应该很多朋友已经反应过来了,上面的就是在
reduce
。对于for
循环那里,我们可以这样写:
var result = arguments[0];
for (var i = 1; i < arguments.length; i++) {
result = getSym(result, arguments[i]);
}
- 这就不难看出,其实就是在迭代
result
,且result
具有初始值arguments[0]
function sym(arg) {
var argArr = [].slice.call(arguments);
return argArr.reduce(function(accum, e) {
return accum.concat(e).filter(function(ele) {
return accum.indexOf(ele) === -1 || e.indexOf(ele) === -1
}).filter(function(e, i, arr) {
return arr.indexOf(e) === i;
});
});
}