How to early break reduce() method?

后端 未结 12 1148
忘掉有多难
忘掉有多难 2020-11-28 05:44

How can I break the iteration of reduce() method?

for:

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


        
相关标签:
12条回答
  • 2020-11-28 06:39

    I solved it like follows, for example in the some method where short circuiting can save a lot:

    const someShort = (list, fn) => {
      let t;
      try {
        return list.reduce((acc, el) => {
          t = fn(el);
          console.log('found ?', el, t)
          if (t) {
            throw ''
          }
          return t
        }, false)
      } catch (e) {
        return t
      }
    }
    
    const someEven = someShort([1, 2, 3, 1, 5], el => el % 2 === 0)
    
    console.log(someEven)

    0 讨论(0)
  • 2020-11-28 06:42

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

    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:

    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);
      });
    }
    
    0 讨论(0)
  • 2020-11-28 06:43

    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
    }
    
    0 讨论(0)
  • 2020-11-28 06:45

    You can use functions like some and every as long as you don't care about the return value. every breaks when the callback returns false, some when it returns true:

    things.every(function(v, i, o) {
      // do stuff 
      if (timeToBreak) {
        return false;
      } else {
        return true;
      }
    }, thisArg);
    
    0 讨论(0)
  • 2020-11-28 06:46

    As the promises have resolve and reject callback arguments, I created the reduce workaround function with the break callback argument. It takes all the same arguments as native reduce method, except the first one is an array to work on (avoid monkey patching). The third [2] initialValue argument is optional. See the snippet below for the function reducer.

    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 reducer as an Array method modified script:

    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);
    
    0 讨论(0)
  • 2020-11-28 06:48

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

    0 讨论(0)
提交回复
热议问题