在多维数组中找到值的最快方法?

时间:2015-12-31 02:24:05

标签: javascript arrays performance

我正在创建一个在多个数组中找到值的函数,但是我遇到了一些性能问题,因为数组可能很长。到目前为止我发现的最佳表现是这样的:

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;
}

我尝试了一些不同的方法,但到目前为止,前一个功能的表现是最好的。

关于如何让它快一点的任何想法?

2 个答案:

答案 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。最初的测试是致命的偏见,因为它搜索一个小数组仅用于单个值,并且该值是最后一行中倒数第二个项目。 (递减循环的第二个项目会找到 - 因此是偏见。)为了反映更真实的场景,现在:

  • 样本数据较大;可配置的10 x 100平方阵列。 (OP表示数组可能非常长。)
  • 每次测试中搜索三个数据点;第一个,最后一个和中间。 (这可以进一步扩展,但这个选择涵盖了基于循环的解决方案的最佳,最差和平均值。)
  • 值是字符串而不是数字;虽然这会稍微抵消搜索算法的影响,因为字符串比较比数字比较慢,但由于OP没有表明值的性质,所以它是一个更现实的假设。

此测试显示所有建议的解决方案表现相似,这强化了@ torazaburo的观点,这些类型的微优化是否值得担心是值得怀疑的。

我建议使用最容易维护的解决方案。如果性能对您来说是一个实际问题,请使用真实反映您情况的数据测试您的选项。

如果您想知道,此解决方案在测试中显示为 concat

test results

答案 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