如何提前中断reduce()方法?

IT技术 javascript loops reduce
2021-02-26 12:15:10

我怎样才能打破reduce()方法的迭代

for

for (var i = Things.length - 1; i >= 0; i--) {
  if(Things[i] <= 0){
    break;
  }
};

reduce()

Things.reduce(function(memo, current){
  if(current <= 0){
    //break ???
    //return; <-- this will return undefined to memo, which is not what I want
  }
}, 0)
6个回答

更新

一些评论员提出了一个很好的观点,即原始数组正在发生变异,以便在.reduce()逻辑内部尽早中断

因此,我通过在调用后续步骤之前添加 a稍微修改了答案,从而生成原始数组的副本。 注意:完成相同任务的类似操作是(不太明确)和扩展运算符性能稍差)。请记住,所有这些都为整体运行时间增加了一个额外的线性时间常数因子 + 1*(O(1))。.slice(0).reduce()slice()[...array]

副本用于保护原始数组免受导致迭代弹出的最终突变。

const array = ['apple', '-pen', '-pineapple', '-pen'];
const x = array
    .slice(0)                         // create copy of "array" for iterating
    .reduce((acc, curr, i, arr) => {
       if (i === 2) arr.splice(1);    // eject early by mutating iterated copy
       return (acc += curr);
    }, '');

console.log("x: ", x, "\noriginal Arr: ", array);
// x:  apple-pen-pineapple
// original Arr:  ['apple', '-pen', '-pineapple', '-pen']


老的

您可以通过改变 reduce 函数的第四个参数:“array”来中断 .reduce() 调用的任何迭代。不需要自定义减少功能。有关参数的完整列表,请参阅文档.reduce()

Array.prototype.reduce((acc, curr, i, array))

第四个参数是被迭代数组

const array = ['apple', '-pen', '-pineapple', '-pen'];
const x = array
.reduce((acc, curr, i, arr) => {
    if(i === 2) arr.splice(1);  // eject early
    return acc += curr;
  }, '');
console.log('x: ', x);  // x:  apple-pen-pineapple

为什么?:

我能想到使用它而不是提供的许多其他解决方案的一个也是唯一的原因是,如果您想为您的算法维护一种函数式编程方法,并且您希望使用最具声明性的方法来实现这一点。如果您的整个目标是将数组从字面上减少为替代的非假原语(字符串、数字、布尔值、符号),那么我认为这实际上是最好的方法。

为什么不?

有一个完整的参数列表,用于不改变函数参数,因为这是一种不好的做法。

这真的是一个糟糕的建议,因为splice执行了一个可见的突变(array)。根据功能范式,您可以在连续传递样式中使用 reduce 或使用带有右关联 reduce 的惰性求值。或者,作为更简单的替代方法,只是简单的递归。
2021-04-23 12:15:10
坚持,稍等! 通过改变reduce 函数的第四个参数:“array”不是正确的语句。在这种情况下,它正在发生(答案中的示例),因为它将数组切割为单长度数组(第一个元素),而它已经达到索引2,显然下一次,对于索引3,它将不会迭代项目(如您正在改变对长度为1 的数组的原始引用)。如果您执行的弹出操作也会改变源数组,但不会在两者之间停止(如果您不在倒数第二个索引处)。
2021-04-23 12:15:10
我更喜欢使用扩展运算符来避免任何不需要的突变,[...array].reduce()
2021-04-28 12:15:10
@KoushikChatterjee 我的陈述对于我隐含的意思是正确的。这对于您的明确含义是不正确的。您应该提出修改语句以包含您的观点的建议,我会进行编辑,因为它会改进整体答案。
2021-05-08 12:15:10
+1。这应该是公认的答案。然而,出于“为什么不”中所述的原因,永远不应使用此解决方案。
2021-05-14 12:15:10

不要使用减少。只需使用普通迭代器(for 等)迭代数组,并在满足条件时中断。

这其中的乐趣在哪里?:)
2021-04-20 12:15:10
是的,可以通过解释为什么如果需要中断循环使用函数式编程可能不是最佳实践来改进这个答案。人们会假设 OP 完全了解基本的迭代器,也许他们只是想避免污染范围,谁知道呢。
2021-04-21 12:15:10
不知道为什么这得到了很多赞成......这是一个非答案,因为 OP 询问如何从 reduce() 中尽早休息......就像当你弯腰时感到疼痛时去看医生,医生告诉你不要弯腰。
2021-04-27 12:15:10
(我在审查期间遇到了这个答案)我认为这个答案有value,应该保留。尽管 OP 可能知道他在做什么想要使用reduce,但其他人可能会发现它有助于寻找解决他们问题的方法(如投票数所示)。
2021-05-07 12:15:10

只要您不关心返回值,您就可以使用someevery 之类的函数当回调返回 false 时每个都会中断,当它返回 true 时会中断一些

things.every(function(v, i, o) {
  // do stuff 
  if (timeToBreak) {
    return false;
  } else {
    return true;
  }
}, thisArg);
但是,如果他试图这样做,reduce那么根据定义,他确实关心返回值。
2021-04-27 12:15:10
@torazaburo——当然,但我没有看到它在 OP 中使用,还有其他方法可以获得结果。;-)
2021-05-01 12:15:10
const isKnownZone = KNOWN_ZONES.some((v) => curZone.substr(v.length) === v)我可以使用reduce,但这不会那么有效。我正在考虑的方式是一些和每个都是布尔函数......一些元素是真的,每个元素都是真的,在集合中
2021-05-05 12:15:10

当然,没有办法让内置版本的reduceto 过早退出。

但是您可以编写自己的 reduce 版本,它使用一个特殊的标记来确定何时应该中断循环。

var EXIT_REDUCE = {};

function reduce(a, f, result) {
  for (let i = 0; i < a.length; i++) {
    let val = f(result, a[i], i, a);
    if (val === EXIT_REDUCE) break;
    result = val;
  }
  return result;
}

像这样使用它,对数组求和,但在达到 99 时退出:

reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0);

> 3
您可以使用惰性求值或 CPS来实现所需的行为:
2021-05-03 12:15:10
这个答案的第一句话是不正确的。你可以休息,详情见下面我的回答。
2021-05-04 12:15:10

Array.every 可以提供一种非常自然的机制来打破高阶迭代。

const product = function(array) {
    let accumulator = 1;
    array.every( factor => {
        accumulator *= factor;
        return !!factor;
    });
    return accumulator;
}
console.log(product([2,2,2,0,2,2]));
// 0

但是你如何在没有突变的情况下做到这一点。
2021-04-23 12:15:10