".forEach索引超出数组长度"

3
我正在CodeFights上玩耍,仍在学习JavaScript。我正在完成其基本挑战之一,但现在遇到了问题。
我并不想要解决方案,而是想要对我的代码出现了什么问题进行说明。代码的目的是简单地返回数组中任何两个相邻元素的最大乘积。我认为每次代码运行时,nextNum变量都会被设置为NaN,因为它是一个不存在的索引。我猜测我混淆了当NaN!= 1时为什么我的代码仍然将highestProduct更改为18,就我所知道,我也不知道18从哪里来,除非它开始再次循环。非常感谢您的帮助。
这是代码:

function adjacentElementsProduct(inputArray) {
    let highestProduct;
    inputArray.forEach(function(loopNum) {
        let nextNum = inputArray.indexOf(loopNum) + 1;
        let newProduct = inputArray[nextNum] * loopNum;
        if (nextNum === 1) {
            highestProduct = newProduct;
        } else if (newProduct > highestProduct) {
            highestProduct = newProduct;
        }
        console.log(highestProduct);
    })
    return highestProduct;
}
let testArr = [3, 6, -2, -5, 7, 3]
console.log(adjacentElementsProduct(testArr));


4
函数(循环数,索引){ let nextNum = index + 1; ... } 该函数接受两个参数:循环数和索引。在函数内部,通过将索引值加1来计算下一个数字的值。 - Patrick Roberts
@GeorgeJempty:OP知道这一点(并且已经提到了)。 - T.J. Crowder
1
+1 @PatrickRoberts 抢先一步了,还有当您处理最后一个元素时,就没有另一个元素在数组中可供比较。 - Sam
@Sam:OP知道这一点并指出了它。 - T.J. Crowder
nextNum 永远不会是 NaN。它可能会超出范围(即最后一个索引 + 1),但在这种特殊情况下,也不会发生,因为数组中的第一个和最后一个元素相同,因此 .indexOf(loopNum) 将返回数组中最后一个元素的 0(提示:对于最后一个元素,nextNum 将为 1)。 - Andreas
@PatrickRoberts:因为它是错的。 :-) 我已经发布了一个新的,希望这次不会再出错。 - T.J. Crowder
3个回答

3
由于这行代码的存在,它会退回到18行:
let nextNum = inputArray.indexOf(loopNum) + 1;

在最后一次循环中,loopNum3。数组中有两个 3,一个在开头,另一个在结尾。当你到达结尾时,indexOf 找到开头的那个 3,返回索引 0,然后加上 1 并赋值给 nextNum,使其变为 1。这意味着以下操作生效:
if (nextNum === 1) {
    highestProduct = newProduct;

...并将highestProduct设置回18,即使在上一个循环中它是21。

正如Patrick Roberts指出的那样,你不需要发现索引,因为forEach回调作为第二个参数接收它。而且正如你所指出的,你意识到自己需要在数组中少停止一个。

因此,在这两个小更改后:

function adjacentElementsProduct(inputArray) {
    let highestProduct;
    inputArray.forEach(function(loopNum, index) {            // ***
        let nextNum = index + 1;                             // ***
        if (nextNum < inputArray.length) {                   // ***
            let newProduct = inputArray[nextNum] * loopNum;
            if (nextNum === 1) {
                highestProduct = newProduct;
            } else if (newProduct > highestProduct) {
                highestProduct = newProduct;
            }
            console.log(highestProduct);
        }
    })
    return highestProduct;
}
let testArr = [3, 6, -2, -5, 7, 3]
console.log(adjacentElementsProduct(testArr));


到目前为止,理解这样的问题最好的方法是使用内置于您的IDE或浏览器中的调试器逐语句地进行调试,同时查看变量等值。


看起来与您先前答案的最后修订版本几乎相同。 - Patrick Roberts
1
@PatrickRoberts:嗯,那个NaN的问题有点偏离主题了... :-) 由于他的indexOf调用,我们从未涉及到NaN... 基本上,当我意识到我的解释是错误的时候,我删除了它,弄清楚了为什么我的解释是错误的以及实际发生了什么,并且发现A)已经过了一段时间,B)问题的关键确实是你的评论。因此发布了一个新的CW,以免成为FGITW... - T.J. Crowder

0

你的代码正在循环中进行,当它到达数组的最后一个元素时,它会拉取下一个元素进行相乘并返回到索引1。这是解释。

nextNum: 1 newProduct: 18 highestProduct: undefined
inputArray[1]: 6 loopNum: 3
nextNum: 2 newProduct: -12 highestProduct: 18
inputArray[2]: -2 loopNum: 6
nextNum: 3 newProduct: 10 highestProduct: 18
inputArray[3]: -5 loopNum: -2
nextNum: 4 newProduct: -35 highestProduct: 18
inputArray[4]: 7 loopNum: -5
nextNum: 5 newProduct: 21 highestProduct: 18
inputArray[5]: 3 loopNum: 7
nextNum: 1 newProduct: 18 highestProduct: 21
inputArray[1]: 6 loopNum: 3
18

0

我想出了一些与@T.J.Crowder的答案非常接近的东西,但我停止发布我的回答了,因为我想要更进一步。

由于主要问题必须使用更好的比较和index作为第二个回调参数来解决,我想指出一个更适合给定情况的数组迭代器方法,而不是forEach... reduce。使用此方法,可以通过自定义回调函数中的逻辑实现更易读且代码量更少。

以下提供的示例代码不应视为对OP的答案,而应视为使用重构和更合适的工具可以完成的示例...

function collectMaximumAdjacentElementsProduct (maxProduct, number, idx, numberList) {
    var
        nextNumber = numberList[idx + 1];

    if (typeof nextNumber !== 'undefined') {
        maxProduct = Math.max(maxProduct, (number * nextNumber));
    }
    return maxProduct;
}

var arr = [3, 6, -2, -5, 7, 3];
var num = arr.reduce(collectMaximumAdjacentElementsProduct, Number.NEGATIVE_INFINITY)

console.log('collectMaximumAdjacentElementsProduct', arr, ':', num);
.as-console-wrapper { max-height: 100%!important; top: 0; }


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