一天一个仿lodash函数实现-xor
xor 数组类异或,其实是找出一堆数组里面,只出现一次的元素集合。
所以在最开始实现时,我的逻辑是,所有数遍历一次组合起来,并且记录每个元素出现的次数,再遍历一次全部,只把出现一次的元素丢到结果里返回。
上述想法是可以实现,但后续要实现xorBy和xorWith就有点不方便.
查看了下lodash源码,它的实现方式利用了difference方法,基本上把by和with转嫁到differenceBy和differenceWith来处理了。
实际逻辑是,两层遍历,外层遍历数组,内层算出每一个数组和其他数组进行difference得出每个数组不一样的部分,最后整合即为结果。
function xor0(...arrays){
const map = {};
const all = arrays.reduce((pre, cur) => {
cur.forEach(item=>{
map[item] = map[item]?map[item]+1:1
})
return pre.concat(cur)
}, [])
let index = 0;
const result = [];
while(index<all.length){
const cur = all[index];
if(map[cur]===1){
result.push(cur);
}
}
return result;
}
function difference(arr, ...rest) {
// 扁平化rest
const target = rest.reduce((pre, cur) => {
return pre.concat(cur)
}, [])
return arr.filter(item => !target.includes(item))
}
function xor(...arrays){
const len = arrays.length;
const result = Array(len);
let index = 0;
while(index<len){
let oIndex = 0;
while(oIndex<len){
if(oIndex!==index){
result[index] = difference(result[index]||arrays[index], arrays[oIndex])
}
oIndex++
}
index++;
}
return result.reduce((pre, cur)=>{
return pre.concat(cur);
}, [])
}
function differenceBy(arr, ...args) {
if(args.length<2) {
throw new Error('iteratee missing or target array missing')
}
const rest = args.slice(0, args.length-1)
const keyOrFunc = args[args.length-1]
let iteratee;
if(typeof keyOrFunc === 'string') {
iteratee = (item) => item[keyOrFunc]
}else{
iteratee = keyOrFunc
}
// 扁平化rest
const target = rest.reduce((pre, cur) => {
return pre.concat(cur.map(i=>iteratee(i)))
}, []);
return arr.filter(item => !target.includes(iteratee(item)))
}
function xorBy(...args){
const iteratee = args[args.length-1];
const it = typeof iteratee === 'string'?x=>x[iteratee]:iteratee;
const arrays = args.slice(0, args.length-1);
const len = arrays.length;
const result = Array(len);
let index = 0;
while(index<len){
let oIndex = 0;
while(oIndex<len){
if(oIndex!==index){
result[index] = differenceBy(result[index]||arrays[index], arrays[oIndex], it)
}
oIndex++
}
index++;
}
return result.reduce((pre, cur)=>{
return pre.concat(cur);
}, [])
}
function differenceWith(arr, ...args) {
if(args.length<2) {
throw new Error('iteratee missing or target array missing')
}
const rest = args.slice(0, args.length-1)
const comparator = args[args.length-1]
// 扁平化rest
const target = rest.reduce((pre, cur) => {
return pre.concat(cur)
}, []);
return arr.filter(item => {
for(let i=0;i<target.length;i++) {
if(comparator(target[i], item)) {
return false;
}
}
return true
})
}
function xorWith(...args){
const iteratee = args[args.length-1];
const arrays = args.slice(0, args.length-1);
const len = arrays.length;
const result = Array(len);
let index = 0;
while(index<len){
let oIndex = 0;
while(oIndex<len){
if(oIndex!==index){
result[index] = differenceWith(result[index]||arrays[index], arrays[oIndex], iteratee)
}
oIndex++
}
index++;
}
return result.reduce((pre, cur)=>{
return pre.concat(cur);
}, [])
}