以上所有解决方案都“起作用”,但并不是最佳性能,并且都以相同的方式解决该问题,即使用Array.prototype.indexOf或Array.prototype.includes在每个点处线性搜索所有条目。更快的解决方案(在大多数情况下,甚至比二进制搜索还要快)将是对数组进行排序,并在前进时跳过,如下所示。但是,缺点是这要求数组中的所有条目都是数字或字符串。但是,在某些罕见情况下,二进制搜索可能比渐进线性搜索更快。这些情况是由于我的渐进线性搜索的复杂度为O(2n 1 + n 2)(仅O(n 1 + n 2)在更快的C / C ++版本中)(其中n 1是搜索到的数组,n 2是过滤器数组),而二进制搜索的复杂度为O(n 1 ceil(log 2 n 2))(ceil =向上舍入-到小区 ING),以及最后的搜索的indexOf之间具有高度可变的复杂性为O(n 1)和为O(n 1 ñ 2),平均出为O(n 1个小区(N 2 ÷2) )。因此,在以下情况下,indexOf平均只会是最快的(N 1, n 2)等于 2){1,2},{1,3}或{x,1 |x∈N}。但是,这仍然不是现代硬件的完美代表。IndexOf在大多数现代浏览器中可以想象到的最大程度进行了本机优化,使其非常受分支预测法则的约束。因此,如果我们对indexOf的假设与渐进线性和二进制搜索相同(即数组已预先排序),那么根据链接中列出的统计数据,我们可以预期IndexOf的速度大约提高6倍,将其复杂度转换为O(n 1 ÷6)和O(n 1 n ,平均为 7÷12))O(n 1 ceil(n 2。最后,请注意,以下解决方案永远无法与对象一起使用,因为JavaScript中的对象无法通过JavaScript中的指针进行比较。
function sortAnyArray(a,b) { return a>b ? 1 : (a===b ? 0 : -1); }
function sortIntArray(a,b) { return (a|0) - (b|0) |0; }
function fastFilter(array, handle) {
var out=[], value=0;
for (var i=0, len=array.length|0; i < len; i=i+1|0)
if (handle(value = array[i]))
out.push( value );
return out;
}
const Math_clz32 = Math.clz32 || (function(log, LN2){
return function(x) {
return 31 - log(x >>> 0) / LN2 | 0; // the "| 0" acts like math.floor
};
})(Math.log, Math.LN2);
/* USAGE:
filterArrayByAnotherArray(
[1,3,5],
[2,3,4]
) yields [1, 5], and it can work with strings too
*/
function filterArrayByAnotherArray(searchArray, filterArray) {
if (
// NOTE: This does not check the whole array. But, if you know
// that there are only strings or numbers (not a mix of
// both) in the array, then this is a safe assumption.
// Always use `==` with `typeof` because browsers can optimize
// the `==` into `===` (ONLY IN THIS CIRCUMSTANCE)
typeof searchArray[0] == "number" &&
typeof filterArray[0] == "number" &&
(searchArray[0]|0) === searchArray[0] &&
(filterArray[0]|0) === filterArray[0]
) {filterArray
// if all entries in both arrays are integers
searchArray.sort(sortIntArray);
filterArray.sort(sortIntArray);
} else {
searchArray.sort(sortAnyArray);
filterArray.sort(sortAnyArray);
}
var searchArrayLen = searchArray.length, filterArrayLen = filterArray.length;
var progressiveLinearComplexity = ((searchArrayLen<<1) + filterArrayLen)>>>0
var binarySearchComplexity= (searchArrayLen * (32-Math_clz32(filterArrayLen-1)))>>>0;
// After computing the complexity, we can predict which algorithm will be the fastest
var i = 0;
if (progressiveLinearComplexity < binarySearchComplexity) {
// Progressive Linear Search
return fastFilter(searchArray, function(currentValue){
while (filterArray[i] < currentValue) i=i+1|0;
// +undefined = NaN, which is always false for <, avoiding an infinite loop
return filterArray[i] !== currentValue;
});
} else {
// Binary Search
return fastFilter(
searchArray,
fastestBinarySearch(filterArray)
);
}
}
// see https://stackoverflow.com/a/44981570/5601591 for implementation
// details about this binary search algorithm
function fastestBinarySearch(array){
var initLen = (array.length|0) - 1 |0;
const compGoto = Math_clz32(initLen) & 31;
return function(sValue) {
var len = initLen |0;
switch (compGoto) {
case 0:
if (len & 0x80000000) {
const nCB = len & 0x80000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 1:
if (len & 0x40000000) {
const nCB = len & 0xc0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 2:
if (len & 0x20000000) {
const nCB = len & 0xe0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 3:
if (len & 0x10000000) {
const nCB = len & 0xf0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 4:
if (len & 0x8000000) {
const nCB = len & 0xf8000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 5:
if (len & 0x4000000) {
const nCB = len & 0xfc000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 6:
if (len & 0x2000000) {
const nCB = len & 0xfe000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 7:
if (len & 0x1000000) {
const nCB = len & 0xff000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 8:
if (len & 0x800000) {
const nCB = len & 0xff800000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 9:
if (len & 0x400000) {
const nCB = len & 0xffc00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 10:
if (len & 0x200000) {
const nCB = len & 0xffe00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 11:
if (len & 0x100000) {
const nCB = len & 0xfff00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 12:
if (len & 0x80000) {
const nCB = len & 0xfff80000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 13:
if (len & 0x40000) {
const nCB = len & 0xfffc0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 14:
if (len & 0x20000) {
const nCB = len & 0xfffe0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 15:
if (len & 0x10000) {
const nCB = len & 0xffff0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 16:
if (len & 0x8000) {
const nCB = len & 0xffff8000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 17:
if (len & 0x4000) {
const nCB = len & 0xffffc000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 18:
if (len & 0x2000) {
const nCB = len & 0xffffe000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 19:
if (len & 0x1000) {
const nCB = len & 0xfffff000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 20:
if (len & 0x800) {
const nCB = len & 0xfffff800;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 21:
if (len & 0x400) {
const nCB = len & 0xfffffc00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 22:
if (len & 0x200) {
const nCB = len & 0xfffffe00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 23:
if (len & 0x100) {
const nCB = len & 0xffffff00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 24:
if (len & 0x80) {
const nCB = len & 0xffffff80;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 25:
if (len & 0x40) {
const nCB = len & 0xffffffc0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 26:
if (len & 0x20) {
const nCB = len & 0xffffffe0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 27:
if (len & 0x10) {
const nCB = len & 0xfffffff0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 28:
if (len & 0x8) {
const nCB = len & 0xfffffff8;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 29:
if (len & 0x4) {
const nCB = len & 0xfffffffc;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 30:
if (len & 0x2) {
const nCB = len & 0xfffffffe;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 31:
if (len & 0x1) {
const nCB = len & 0xffffffff;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
}
// MODIFICATION: Instead of returning the index, this binary search
// instead returns whether something was found or not.
if (array[len|0] !== sValue) {
return true; // preserve the value at this index
} else {
return false; // eliminate the value at this index
}
};
}
请参阅我的其他文章,以了解有关所用二进制搜索算法的更多详细信息
如果您对文件大小有些担心(我尊重),那么您可能会牺牲一些性能,以便大大减小文件大小并增加可维护性。
function sortAnyArray(a,b) { return a>b ? 1 : (a===b ? 0 : -1); }
function sortIntArray(a,b) { return (a|0) - (b|0) |0; }
function fastFilter(array, handle) {
var out=[], value=0;
for (var i=0, len=array.length|0; i < len; i=i+1|0)
if (handle(value = array[i]))
out.push( value );
return out;
}
/* USAGE:
filterArrayByAnotherArray(
[1,3,5],
[2,3,4]
) yields [1, 5], and it can work with strings too
*/
function filterArrayByAnotherArray(searchArray, filterArray) {
if (
// NOTE: This does not check the whole array. But, if you know
// that there are only strings or numbers (not a mix of
// both) in the array, then this is a safe assumption.
typeof searchArray[0] == "number" &&
typeof filterArray[0] == "number" &&
(searchArray[0]|0) === searchArray[0] &&
(filterArray[0]|0) === filterArray[0]
) {
// if all entries in both arrays are integers
searchArray.sort(sortIntArray);
filterArray.sort(sortIntArray);
} else {
searchArray.sort(sortAnyArray);
filterArray.sort(sortAnyArray);
}
// Progressive Linear Search
var i = 0;
return fastFilter(searchArray, function(currentValue){
while (filterArray[i] < currentValue) i=i+1|0;
// +undefined = NaN, which is always false for <, avoiding an infinite loop
return filterArray[i] !== currentValue;
});
}
为了证明速度的差异,让我们研究一些JSPerfs。对于过滤由16个元素组成的数组,二进制搜索比indexOf快大约17%,而filterArrayByAnotherArray比indexOf快93%。对于过滤256个元素的数组,二进制搜索比indexOf快大约291%,而filterArrayByAnotherArray比indexOf快353%。用于过滤4096个元素的数组,二进制搜索比indexOf快大约2655%,而filterArrayByAnotherArray比indexOf快大约4627%。
反向滤波(与门类似)
上一节提供了获取数组A和数组B并删除A中存在于B中的所有元素的代码:
filterArrayByAnotherArray(
[1,3,5],
[2,3,4]
);
// yields [1, 5]
下一部分将提供用于反向过滤的代码,其中我们从A中删除了B中不存在的所有元素。此过程在功能上等同于仅保留A和B共同的元素,例如AND门:
reverseFilterArrayByAnotherArray(
[1,3,5],
[2,3,4]
);
// yields [3]
这是反向过滤的代码:
function sortAnyArray(a,b) { return a>b ? 1 : (a===b ? 0 : -1); }
function sortIntArray(a,b) { return (a|0) - (b|0) |0; }
function fastFilter(array, handle) {
var out=[], value=0;
for (var i=0, len=array.length|0; i < len; i=i+1|0)
if (handle(value = array[i]))
out.push( value );
return out;
}
const Math_clz32 = Math.clz32 || (function(log, LN2){
return function(x) {
return 31 - log(x >>> 0) / LN2 | 0; // the "| 0" acts like math.floor
};
})(Math.log, Math.LN2);
/* USAGE:
reverseFilterArrayByAnotherArray(
[1,3,5],
[2,3,4]
) yields [3], and it can work with strings too
*/
function reverseFilterArrayByAnotherArray(searchArray, filterArray) {
if (
// NOTE: This does not check the whole array. But, if you know
// that there are only strings or numbers (not a mix of
// both) in the array, then this is a safe assumption.
// Always use `==` with `typeof` because browsers can optimize
// the `==` into `===` (ONLY IN THIS CIRCUMSTANCE)
typeof searchArray[0] == "number" &&
typeof filterArray[0] == "number" &&
(searchArray[0]|0) === searchArray[0] &&
(filterArray[0]|0) === filterArray[0]
) {filterArray
// if all entries in both arrays are integers
searchArray.sort(sortIntArray);
filterArray.sort(sortIntArray);
} else {
searchArray.sort(sortAnyArray);
filterArray.sort(sortAnyArray);
}
var searchArrayLen = searchArray.length, filterArrayLen = filterArray.length;
var progressiveLinearComplexity = ((searchArrayLen<<1) + filterArrayLen)>>>0
var binarySearchComplexity= (searchArrayLen * (32-Math_clz32(filterArrayLen-1)))>>>0;
// After computing the complexity, we can predict which algorithm will be the fastest
var i = 0;
if (progressiveLinearComplexity < binarySearchComplexity) {
// Progressive Linear Search
return fastFilter(searchArray, function(currentValue){
while (filterArray[i] < currentValue) i=i+1|0;
// +undefined = NaN, which is always false for <, avoiding an infinite loop
// For reverse filterning, I changed !== to ===
return filterArray[i] === currentValue;
});
} else {
// Binary Search
return fastFilter(
searchArray,
inverseFastestBinarySearch(filterArray)
);
}
}
// see https://stackoverflow.com/a/44981570/5601591 for implementation
// details about this binary search algorithim
function inverseFastestBinarySearch(array){
var initLen = (array.length|0) - 1 |0;
const compGoto = Math_clz32(initLen) & 31;
return function(sValue) {
var len = initLen |0;
switch (compGoto) {
case 0:
if (len & 0x80000000) {
const nCB = len & 0x80000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 1:
if (len & 0x40000000) {
const nCB = len & 0xc0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 2:
if (len & 0x20000000) {
const nCB = len & 0xe0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 3:
if (len & 0x10000000) {
const nCB = len & 0xf0000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 4:
if (len & 0x8000000) {
const nCB = len & 0xf8000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 5:
if (len & 0x4000000) {
const nCB = len & 0xfc000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 6:
if (len & 0x2000000) {
const nCB = len & 0xfe000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 7:
if (len & 0x1000000) {
const nCB = len & 0xff000000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 8:
if (len & 0x800000) {
const nCB = len & 0xff800000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 9:
if (len & 0x400000) {
const nCB = len & 0xffc00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 10:
if (len & 0x200000) {
const nCB = len & 0xffe00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 11:
if (len & 0x100000) {
const nCB = len & 0xfff00000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 12:
if (len & 0x80000) {
const nCB = len & 0xfff80000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 13:
if (len & 0x40000) {
const nCB = len & 0xfffc0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 14:
if (len & 0x20000) {
const nCB = len & 0xfffe0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 15:
if (len & 0x10000) {
const nCB = len & 0xffff0000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 16:
if (len & 0x8000) {
const nCB = len & 0xffff8000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 17:
if (len & 0x4000) {
const nCB = len & 0xffffc000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 18:
if (len & 0x2000) {
const nCB = len & 0xffffe000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 19:
if (len & 0x1000) {
const nCB = len & 0xfffff000;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 20:
if (len & 0x800) {
const nCB = len & 0xfffff800;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 21:
if (len & 0x400) {
const nCB = len & 0xfffffc00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 22:
if (len & 0x200) {
const nCB = len & 0xfffffe00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 23:
if (len & 0x100) {
const nCB = len & 0xffffff00;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 24:
if (len & 0x80) {
const nCB = len & 0xffffff80;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 25:
if (len & 0x40) {
const nCB = len & 0xffffffc0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 26:
if (len & 0x20) {
const nCB = len & 0xffffffe0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 27:
if (len & 0x10) {
const nCB = len & 0xfffffff0;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 28:
if (len & 0x8) {
const nCB = len & 0xfffffff8;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 29:
if (len & 0x4) {
const nCB = len & 0xfffffffc;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 30:
if (len & 0x2) {
const nCB = len & 0xfffffffe;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
case 31:
if (len & 0x1) {
const nCB = len & 0xffffffff;
len ^= (len ^ (nCB-1)) & ((array[nCB] <= sValue |0) - 1 >>>0);
}
}
// MODIFICATION: Instead of returning the index, this binary search
// instead returns whether something was found or not.
// For reverse filterning, I swapped true with false and vice-versa
if (array[len|0] !== sValue) {
return false; // preserve the value at this index
} else {
return true; // eliminate the value at this index
}
};
}
有关反向过滤代码的较慢的较小版本,请参见下文。
function sortAnyArray(a,b) { return a>b ? 1 : (a===b ? 0 : -1); }
function sortIntArray(a,b) { return (a|0) - (b|0) |0; }
function fastFilter(array, handle) {
var out=[], value=0;
for (var i=0, len=array.length|0; i < len; i=i+1|0)
if (handle(value = array[i]))
out.push( value );
return out;
}
/* USAGE:
reverseFilterArrayByAnotherArray(
[1,3,5],
[2,3,4]
) yields [3], and it can work with strings too
*/
function reverseFilterArrayByAnotherArray(searchArray, filterArray) {
if (
// NOTE: This does not check the whole array. But, if you know
// that there are only strings or numbers (not a mix of
// both) in the array, then this is a safe assumption.
typeof searchArray[0] == "number" &&
typeof filterArray[0] == "number" &&
(searchArray[0]|0) === searchArray[0] &&
(filterArray[0]|0) === filterArray[0]
) {
// if all entries in both arrays are integers
searchArray.sort(sortIntArray);
filterArray.sort(sortIntArray);
} else {
searchArray.sort(sortAnyArray);
filterArray.sort(sortAnyArray);
}
// Progressive Linear Search
var i = 0;
return fastFilter(searchArray, function(currentValue){
while (filterArray[i] < currentValue) i=i+1|0;
// +undefined = NaN, which is always false for <, avoiding an infinite loop
// For reverse filter, I changed !== to ===
return filterArray[i] === currentValue;
});
}
return arrTwo.indexOf(e) === -1;
代码:var filteredArr = firstArr.filter(el => secondArr.indexOf(el) === -1);