Javascript 如何打破reduce方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36144406/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:41:56  来源:igfitidea点击:

How to break on reduce method

javascriptloopsreduce

提问by Julio Marins

How can I break the iteration on reduce method?

如何打破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)

采纳答案by Tobiah Rex

UPDATE

更新

Some of the commentators make a good point that the original array is being mutated in order to break early inside the .reduce()logic.

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

Therefore, I've modified the answer slightlyby adding a .slice(0)before calling a follow-on .reduce()step, yielding a copy of the original array. NOTE: Similar ops that accomplish the same task are slice()(less explicit), and spread operator [...array](slightly less performant). Bear in mind, all of these add an additional constant factor of linear time to the overall runtime + 1*(O(1)).

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

The copy, serves to preserve the original array from the eventual mutation that causes ejection from iteration.

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

const array = ['9', '91', '95', '96', '99'];
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:  99195
// original Arr:  [ '9', '91', '95', '96', '99' ]


OLD

老的

You CAN break on any iteration of a .reduce() invocation by mutating the 4th argument of the reduce function: "array". No need for a custom reduce function. See Docsfor full list of .reduce()parameters.

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

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

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

The 4th argument is the arraybeing iterated over.

第四个参数是被迭代的数组

const array = ['9', '91', '95', '96', '99'];
const x = array
.reduce((acc, curr, i, arr) => {
    if(i === 2) arr.splice(1);  // eject early
    return acc += curr;
  }, '');
console.log('x: ', x);  // x:  99195

WHY?:

为什么?:

The 1 and only reason I can think of to use this instead of the many other solutions presented is if you want to maintain a functional programming methodology to your algo, and you want the most declarative approach possible to accomplish that. If your entire goal is to literally REDUCE an array to an alternate non-falsey primitive (String, Number, Boolean, Symbol) then I would argue this IS in fact, the best approach.

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

WHY NOT?

为什么不?

There's a whole list of arguments to make for NOT mutating function parameters as it's a bad practice.

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

回答by RobG

You can use functions like someand everyas long as you don't care about the return value. everybreaks when the callback returns false, somewhen it returns true:

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

things.every(function(v, i, o) {
  // do stuff 
  if (timeToBreak) {
    return false;
  } else {
    return true;
  }
}, thisArg);

回答by AndroidDev

Don't use reduce. Just iterate on the array with normal iterators (for, etc) and break out when your condition is met.

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

回答by AndroidDev

There is no way, of course, to get the built-in version of reduceto exit prematurely.

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

But you can write your own version of reduce which uses a special token to identify when the loop should be broken.

但是您可以编写自己的 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;
}

Use it like this, to sum an array but exit when you hit 99:

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

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

> 3

回答by Doug Coburn

Array.every can provide a very natural mechanism for breaking out of high order iteration.

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

回答by Koudela

You can break every code - and thus every build in iterator - by throwing an exception:

您可以通过抛出异常来破坏每个代码 - 从而破坏迭代器中的每个构建:

function breakReduceException(value) {
    this.value = value
}

try {
    Things.reduce(function(memo, current) {
        ...
        if (current <= 0) throw new breakReduceException(memo)
        ...
    }, 0)
} catch (e) {
    if (e instanceof breakReduceException) var memo = e.value
    else throw e
}

回答by alun

Another simple implementation that I came with solving the same issue:

我解决相同问题的另一个简单实现:

function reduce(array, reducer, first) {
  let result = first || array.shift()

  while (array.length > 0) {
    result = reducer(result, array.shift())
    if (result && result.reduced) {
      return result.reduced
    }
  }

  return result
}

回答by Erik Waters

You cannot break from inside of a reduce method. Depending on what you are trying to accomplish you could alter the final result (which is one reason you may want to do this)

您不能从 reduce 方法内部中断。根据您要完成的工作,您可以更改最终结果(这是您可能想要这样做的原因之一)

[1, 1, 1].reduce((a, b) => a + b, 0); // returns 3

[1, 1, 1].reduce((a, b, c, d) => {
  if (c === 1 && b < 3) {
    return a + b + 1;
  } 
  return a + b;
}, 0); // now returns 4

Keep in mind: you cannot reassign the array parameter directly

请记住:您不能直接重新分配数组参数

[1, 1, 1].reduce( (a, b, c, d) => {
  if (c === 0) {
    d = [1, 1, 2];
  } 
  return a + b;
}, 0); // still returns 3

however (as pointed out below) you CAN affect the outcome by changing the array's contents:

但是(如下所述)您可以通过更改数组的内容来影响结果:

[1, 1, 1].reduce( (a, b, c, d) => {
  if (c === 0) {
    d[2] = 100;
  } 
  return a + b;
}, 0); // now returns 102

回答by Pawe?

As the promises have resolveand rejectcallback arguments, I created the reduceworkaround function with the breakcallback argument. It takes all the same arguments as native reducemethod, except the first one is an array to work on (avoid monkey patching). The third [2] initialValueargument is optional. See the snippet below for the functionreducer.

由于promise■找resolvereject回调的论据,我创建了reduce与解决方法函数break回调参数。它采用与本机reduce方法相同的所有参数,除了第一个是要处理的数组(避免猴子修补)。第三个 [2]initialValue参数是可选的。有关function减速器,请参阅下面的代码段。

var list = ["w","o","r","l","d"," ","p","i","e","r","o","g","i"];

var result = reducer(list,(total,current,index,arr,stop)=>{
  if(current === " ") stop(); //when called, the loop breaks
  return total + current;
},'hello ');

console.log(result); //hello world

function reducer(arr, callback, initial) {
  var hasInitial = arguments.length >= 3;
  var total = hasInitial ? initial : arr[0];
  var breakNow = false;
  for (var i = hasInitial ? 0 : 1; i < arr.length; i++) {
    var currentValue = arr[i];
    var currentIndex = i;
    var newTotal = callback(total, currentValue, currentIndex, arr, () => breakNow = true);
    if (breakNow) break;
    total = newTotal;
  }
  return total;
}

And here is the reduceras an Array methodmodified script:

这是reducer作为数组method修改的脚本:

Array.prototype.reducer = function(callback,initial){
  var hasInitial = arguments.length >= 2;
  var total = hasInitial ? initial : this[0];
  var breakNow = false;
  for (var i = hasInitial ? 0 : 1; i < this.length; i++) {
    var currentValue = this[i];
    var currentIndex = i;
    var newTotal = callback(total, currentValue, currentIndex, this, () => breakNow = true);
    if (breakNow) break;
    total = newTotal;
  }
  return total;
};

var list = ["w","o","r","l","d"," ","p","i","e","r","o","g","i"];

var result = list.reducer((total,current,index,arr,stop)=>{
  if(current === " ") stop(); //when called, the loop breaks
  return total + current;
},'hello ');


console.log(result);

回答by luxigo

If you want to chain promises sequentially with reduce using the pattern below:

如果您想使用以下模式将 promise 与 reduce 顺序链接:

return [1,2,3,4].reduce(function(promise,n,i,arr){
   return promise.then(function(){
       // this code is executed when the reduce loop is terminated,
       // so truncating arr here or in the call below does not works
       return somethingReturningAPromise(n);
   });
}, Promise.resolve());

But need to break according to something happening inside or outside a promise things become a little bit more complicated because the reduce loop is terminated before the first promise is executed, making truncating the array in the promise callbacks useless, I ended up with this implementation:

但是需要根据promise内部或外部发生的事情来中断事情变得有点复杂,因为reduce循环在第一个promise执行之前终止,使得在promise回调中截断数组无用,我最终得到了这个实现:

function reduce(array, promise, fn, i) {
  i=i||0;
  return promise
  .then(function(){
    return fn(promise,array[i]);
  })
  .then(function(result){
    if (!promise.break && ++i<array.length) {
      return reduce(array,promise,fn,i);
    } else {
      return result;
    }
  })
}

Then you can do something like this:

然后你可以做这样的事情:

var promise=Promise.resolve();
reduce([1,2,3,4],promise,function(promise,val){
  return iter(promise, val);
}).catch(console.error);

function iter(promise, val) {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      if (promise.break) return reject('break');
      console.log(val);
      if (val==3) {promise.break=true;}
      resolve(val);
    }, 4000-1000*val);
  });
}