JavaScript中查找数组中元素最高效的方法是什么?并解释这段棘手的代码。

4

什么是最有效的在数组中查找项目的方式,这种方式是逻辑和网页开发人员都能理解的呢?

我发现了这段代码:

var inArray = function(a, b, c, d) {
    for (c in b) d |= b[c] === a;
    return !!d
};

它运行良好。请有人为我解释一下这段代码吗?
我也遇到了一个完全相同的问题,可能会使我的问题成为重复的问题。但是我的真正问题在于上述代码的解释以及为什么使用了位运算符。
此外,是否有一种方法可以在不使用任何for循环或迭代的情况下获取数组中项目的索引?


3
数组上的for...in?厉害!?在我看来,这段代码令人十分反感。 - Elias Van Ootegem
@EliasVanOotegem,我改变了我的问题。谢谢 - Om Shankar
4个回答

5
var inArray = function(a, b, c, d) {
    for (c in b) d |= b[c] === a;
    return !!d
};

那是一段可怕的代码,你应该远离它。这里完全不需要位运算符,并且 c 和 d 作为参数根本毫无意义(正如 Raymond Chen 指出的那样,代码作者可能这样做是为了节省声明本地变量的空间--但问题在于如果将 true 传递给 d,代码就会突然崩溃,而额外的参数破坏了扫视声明时提供的任何理解)。
我会解释代码,但首先,以下是更好的选项:
function inArray(arr, obj) {
    for (var i = 0; i < arr.length; ++i) {
        if (arr[i] === obj) {
            return true;
        }
    }
    return false;
}

注意,这取决于数组是实际的数组。你可以使用类似 for (k in arr) 的循环来将其推广至所有对象。
无论如何,接下来进行解释:
for (c in b) d |= b[c] === a;

这意味着对于 b 中的每个键(存储在 c 中),我们将检查 b [c] === a 。换句话说,我们正在对数组进行线性扫描,并检查每个元素是否与 a 相等。 d | = val 是按位或运算符。 val 中高位的位将在 d 中设置为高位。这在比JS更暴露位的语言中更容易说明,但可以用简单的例子来说明:
10011011
01000001
--------
11011011

它只是将每个位与另一个值的相同位置位进行OR运算。

滥用它的原因在于它使代码变得混乱,并且依赖于奇怪的隐式转换。

x === y返回一个布尔值。在按位表达式中使用布尔值几乎没有意义。但实际上,布尔值被转换为非零值(可能是1)。

同样地,undefinedd的值。这意味着d将被转换为进行位运算的数字0。

0 | 0 = 0,但0 | 1 = 1。所以基本上它是一种精美的形式:

for (c in b) d = (d || (b[c] === a));

关于!!x的用法是将某个值强制转换为布尔型。而!x会将x隐式地转换为布尔型,再对其取反。额外的!符号再次取反结果。因此,!!x被解读为true,意味着x至少是宽松的真(1"string"等),而!!x则暗示x至少是宽松的假(0""等)。
这个答案提供了几个选项,但请注意,所有这些选项都是为了回退到本地的indexOf函数,该函数几乎肯定比我们在代码中编写的任何内容都要快。

将c和d声明为参数是一种懒惰的方式,它们被声明为局部变量,并带有这样的风险:如果有人使用四个参数进行调用,则d的初始值将是错误的! - Raymond Chen
@RaymondChen 啊,我想这就是为什么它被这样做的原因。那个可怕小片段的作者显然决心要让它愚蠢地紧凑 :). 我甚至从未想过他们这样做是为了节省一些字符。 - Corbin

2

这段代码写得相当糟糕,几乎无法阅读。我不会把它归为“棒极了”的范畴……

以下是重写后的版本,希望更易于阅读:

function inArray (aElt, aArray) {
  var key;
  var ret = false;
  for (key in aArray)
    ret = ret || (aArray[key] === aElt);
  return ret;
}

使用for循环似乎是最自然的方法。虽然可以使用递归来完成,但这不像在这里使用最简单的方法。


2
在数组中存在元素且不是最后一个元素的情况下,可以重写代码以显著提高效率。当找到该元素时,只需返回true,否则在函数底部返回false。这样for循环在找到元素时就会短路。 - Corbin
这是不正确的。x = 1; x |= 2; x; // 3|= 是一个二进制或赋值运算符,而不是逻辑或。 - Paul S.
@PaulS.:是的,但===只能评估为真或假,这映射到数字1和0。对于这个有限的值范围,|||实际上是相同的(除了短路评估)。 - Ilmari Karonen
我放置的代码是一行代码 var inArray = function(a,b,c,d){for(c in b)d|=b[c]===a;return!!d };,刚刚被某个模块修改得更好看了。它只有一行让我觉得很棒。 - Om Shankar
@OmShankar 等你花了三个小时调试某个东西,最终发现它只是别人一行代码的错误时,你就会爱上一行代码了。然后你会对它们充满热情。 - Corbin
显示剩余4条评论

2

什么是最有效的在数组中查找一个项目的方法,这个方法对于Web开发人员来说是逻辑和易懂的?

可以使用原生方法 haystack.indexOf(needle) !== -1 或者循环方法,只要能够返回结果就立即返回。

function inArray(needle, haystack) {
    var i = haystack.length;
    while (i) if (haystack[--i] === needle) return true;
    return false;
};

那是一个非常“C”风格的while循环。使用for循环会更易读,当然这只是个人观点。 - Corbin
@Corbin 我不认为我曾经写过C。我只知道while非常快,而且n !== 0 == true - Paul S.
一个 for 循环本质上就是一个 while 循环。计算机没有 for 循环的概念(甚至没有 while 循环)。类似构造的 for 循环,或者 while (i > 0) 循环将具有相同的性能(或者差异非常小,以至于在任何比较中都可以忽略不计)。虽然我只是挑剔(和自以为是),但无论哪种方式,+1。 - Corbin
@PaulS。在这种情况下,位运算符甚至更快。因此,创建这种疑问的一行代码可能是最快的。我还想要一个逻辑上,被Web开发人员理解的解决方案。原因是,像我这样没有计算机科学背景的Web开发人员也存在 :) - Om Shankar
@OmShankar 如果你不理解我回答中的任何部分,请问我。这个按位方法有两个问题:1是循环不会在最终的 ret 确定后立即终止,第二个按位方法在 JavaScript 中并不总是最快的选择,因为所有变量在内部实际上都是结构体。 - Paul S.
显示剩余4条评论

2

您要求提供递归版本:

var inArray = function(arr, needle, index) {
    index = index || 0;
    if (index >= arr.length)
        return false;
    return arr[index] === needle
         ? true
         : inArray(arr, needle, index + 1);
}

console.log(inArray([1,5,9], 9));

将“contains”更改为“inArray”。 - James
+1。我不需要它,只是想检查一下如何按照理论“所有迭代都可以转换为递归”进行实现。 - Om Shankar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接