我正在创建一个在多个数组中找到值的函数,但是我遇到了一些性能问题,因为数组可能很长。到目前为止我发现的最佳表现是这样的:
function isValInArray(arr, value) {
var bool = false;
var myArr = arr;
var myVal = value;
var i = 0;
var j = 0;
for (i = 0; i < myArr.length; i++) {
for (j = 0; j < myArr[i].length; j++){
if (myArr[i][j] === myVal ) {
bool = true;
}
}
}
return bool;
}
我尝试了一些不同的方法,但到目前为止,前一个功能的表现是最好的。
关于如何让它快一点的任何想法?
答案 0 :(得分:2)
由于您只关心某个值是否在数组中,您可以先将它们展平,然后使用本机函数只执行一次搜索。
function isValInArray(arr, value) {
var myArr = [].concat.apply([], arr); // from stackoverflow.com/a/10865042/1677912
return ( myArr.indexOf(value) >= 0 );
}
我修改了@torazaburo提出的jsPerf测试,见revision 2。最初的测试是致命的偏见,因为它搜索一个小数组仅用于单个值,并且该值是最后一行中倒数第二个项目。 (递减循环的第二个项目会找到 - 因此是偏见。)为了反映更真实的场景,现在:
此测试显示所有建议的解决方案表现相似,这强化了@ torazaburo的观点,这些类型的微优化是否值得担心是值得怀疑的。
我建议使用最容易维护的解决方案。如果性能对您来说是一个实际问题,请使用真实反映您情况的数据测试您的选项。
如果您想知道,此解决方案在测试中显示为 concat 。
答案 1 :(得分:1)
您可以在找到匹配项后立即返回true
:
function isValInArray(arr, value) {
for (var i = 0; i < arr.length; ++i)
for (var j = 0; j < arr[i].length; ++j)
if (arr[i][j] === value)
return true;
return false;
}
在ECMAScript5中,它可以被重写为更具语义性(但可能更慢)的
function isValInArray(arr, value) {
return arr.some(function(sub) {
return sub.indexOf(value) > -1;
});
}
然而,渐近地,两者的平均成本和最差成本仍然与问题中的代码相同,因为数组中的搜索是线性的。然后,如果数组包含n
子数组,每个子数组都有m
项,则费用为n m
。
如果您想加快速度,可以使用ECMAScript 6套。搜索要求平均为次线性。例如,如果实现使用哈希,它们将是常量,因此总成本将为n
。
然后该函数将是以下之一
function isValInArray(arrSets, value) {
return arrSets.some(set => set.has(value));
}
function isValInArray(arrSets, value) {
for (var i = 0; i < arrSets.length; ++i)
if (arrSets[i].has(value)) return true;
return false;
}
示例:
var arrSets = [new Set([1,2,3]), new Set([3,5,6])];
isValInArray(arrSets, 0); // false
isValInArray(arrSets, 1); // true
如果您因为要保留索引而必须使用数组,则可以在搜索之前进行转换。但这将花费n m
,因此只有在您可以重复使用这些集合时才会有用,因为您希望进行大量搜索。
var arrSets = arr.map(sub => new Set(sub));
但在这种情况下,您不需要将这些集分开。与@ Mogsdad proposed类似,您可以在一个集合中插入所有数组的元素,这也将花费n m
。优点是搜索将是不变的。例如:
var arr = [[1,2,3], [3,5,6]],
set = new Set([].concat.apply([], arr));
set.has(0); // false
set.has(1); // true