Javascript - 通过 reduce 方法计算数组元素直到出现特定值不会给出正确的输出

Javascript - Counting array elements by reduce method until specific value occurs doesn't give a correct output

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total)=>(num==0 ? total : total+num), 0) 
console.log(sum(arr, 0))

请检查我如何让它工作。犯了一些错误,但不知道到底是什么。输出是一个函数而不是结果。

调用如下:

console.log(sum(arr, 0)());

执行函数需要括号()

sum(arr, 0)

没有括号,您在变量中存储了对函数的引用

由于您需要在数组的中途停止对值求和,这可能最简单地使用 for 循环实现:

const arr = [5, 6, 0, 7, 8];
const num = 0;

let sum = 0;
for (let i = 0; i < arr.length; i++) {
  if (arr[i] == num) break;
  sum += arr[i];
}
console.log(sum);

如果你想使用 reduce,你需要保留一个标志,说明你是否已经看到 num 值,这样你就可以停止从数组中添加值:

const arr = [5, 6, 0, 7, 8];

const sum = (arr, num) => {
  let seen = false;
  return arr.reduce((c, v) => {
    if (seen || v == num) {
      seen = true;
      return c;
    }
    return c + v;
  }, 0);
}

console.log(sum(arr, 0));
console.log(sum(arr, 8));

这在 .reduce 中很难做到,因为它遍历了 整个 数组。如果我们做一个简单的实现,你会看到问题:

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=>(num==x ? total : total+x), 0) 
console.log(sum(arr, 0))

我们现在正确地进行检查 - 当 x 为零(num 的值)时,num==x 将 return true。然而,结果是错误的,因为这只是 returns true once 但任何其他迭代它仍然是 true。这是同样的事情,更多的日志记录描述了过程的每个步骤:

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=> {
  const boolCheck = num==x;
  const result = boolCheck ? total : total+x;
  console.log(
`total: ${total}
num: ${num}
x: ${x}
boolCheck: ${boolCheck}
result: ${result}`);

    return result;
  }, 0) 
console.log(sum(arr, 0))

因此,您需要添加一些在迭代之间持续存在的标志,这样它就不会丢失。

一个选项是在 reduce 回调中更改外部标志:

const arr = [5,6,0,7,8];
const sum = (arr,num) => {
  let finished = false;
  return arr.reduce((total, x) => {
    if(x === num)
      finished = true;
      
    return finished ? total : total+x;
  }, 0)
}
console.log(sum(arr, 0))

或者,您可以在 reduce 回调内部设置该标志,并在调用之间传递它。它最终以相同的方式工作,但使回调函数变得纯粹。以一些非正统的构造为代价:

const arr = [5,6,0,7,8];
const sum = (arr,num) => {
  return arr.reduce(({total, finished}, x) => {
    if(x === num)
      finished = true;

    total = finished ? total : total+x;

    return {total, finished};
  }, {total: 0, finished: false})
  .total
}
console.log(sum(arr, 0))

如果你想使用reduce但你可以使用其他方法,那么你可以使用Array#indexOf to find the first instance of a value and Array#slice包含任意值到目标值的数组:

const arr = [5,6,0,7,8];
const sum = (arr,num) => {
  const endIndex = arr.indexOf(num);
  
  return arr.slice(0, endIndex)
    .reduce((total, x)=> total+x, 0)
}
console.log(sum(arr, 0))

或作为一个链式表达式:

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr
  .slice(0, arr.indexOf(num))
  .reduce((total, x)=> total+x, 0);

console.log(sum(arr, 0))

其他图书馆可能有一个 takeUntiltakeWhile 操作,它更接近您想要的 - 它让您从 开始 最多一个给定的值或条件。然后你可以减少那个结果。

这是一个使用 Lodash#takeWhile

的示例

通过在这里使用链接,Lodash 将进行惰性求值,因此它只会遍历一次数组,而不是扫描一次找到结束索引并再次遍历数组求和。

const arr = [5,6,0,7,8];
const sum = (arr,num) => _(arr)
  .takeWhile(x => x !== num)
  .reduce((total, x)=>total+x, 0)

console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>

请注意,如果您正在 使用 Lodash,那么您也可以使用 _.sum()。我在上面并不是为了说明通用 takeUntil/takeWhile 的外观。

const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => _(arr)
  .takeWhile(x => x !== num)
  .sum()

console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>