web-dev-qa-db-fra.com

Comment casser la méthode réduire

Comment puis-je rompre l'itération sur la méthode réduire?

for

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

réduire

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

METTRE &AGRAVE; JOUR

Certains commentateurs soulignent avec raison que le tableau d'origine est en cours de mutation afin de pouvoir effectuer une rupture précoce dans la logique .reduce()

Par conséquent, j'ai modifié la réponse légèrement en ajoutant une .slice(0) avant d'appeler une étape suivante .reduce()

Cela permet de conserver le tableau d'origine en copiant son contenu dans un temps linéaire - O (n). Le tableau d'origine est également connecté à la console pour prouver qu'il a été préservé.

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

VIEUX

Vous POUVEZ interrompre n'importe quelle itération d'une invocation .reduce () en modifiant le 4ème argument de la fonction de réduction: "array". Pas besoin d'une fonction de réduction personnalisée. Voir Docs pour la liste complète des paramètres .reduce().

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

Le 4ème argument est le tableau qui est itéré.

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

POURQUOI?:

La seule et unique raison pour laquelle je peux penser à utiliser cela à la place des nombreuses autres solutions présentées est si vous souhaitez conserver une méthodologie de programmation fonctionnelle pour votre algo et que vous souhaitez utiliser l'approche la plus déclarative possible. Si votre objectif est de RÉDUIRE littéralement un tableau en une primitive alternative non-falsey (String, Number, Boolean, Symbol), je dirais que ceci IS en fait, la meilleure approche.

POURQUOI PAS?

Il y a toute une liste d'arguments à faire pour NE PAS modifier les paramètres de fonction car c'est une mauvaise pratique.

47
Tobiah Rex

Vous pouvez utiliser des fonctions telles que some et every tant que vous ne vous souciez pas de la valeur de retour. every rompt lorsque le rappel retourne false, some lorsqu'il renvoie true:

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

Bien entendu, il n’ya aucun moyen d’obtenir la sortie prématurée de la version intégrée de reduce.

Mais vous pouvez écrire votre propre version de reduction qui utilise un jeton spécial pour identifier le moment où la boucle doit être cassée.

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;
}

Utilisez-le comme ceci, pour résumer un tableau, mais quittez quand vous frappez 99:

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

> 3
6
user663031

Ne pas utiliser réduire. Il suffit de parcourir le tableau avec des itérateurs normaux (pour, etc.) et de s’écarter lorsque votre condition est remplie.

5
AndroidDev

Vous pouvez casser chaque code - et donc chaque générateur intégré dans l'itérateur - en lançant une 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
}
4
Koudela

Array.every peut fournir un mécanisme très naturel pour rompre l'itération d'ordre élevé.

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

1
Doug Coburn

Comme les promises ont les arguments de rappel resolve et reject, j'ai créé la fonction de contournement reduce avec l'argument de rappel break. Elle prend tous les mêmes arguments que la méthode native reduce, à la différence que le premier est un tableau sur lequel travailler (évitez les correctifs monkey). Le troisième argument [2] initialValue est facultatif. Voir l'extrait ci-dessous pour le réducteur 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;
}

Et voici le reducer en tant que script modifié de tableau 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);
0
Paweł

Vous ne pouvez pas sortir de l'intérieur d'une méthode de réduction. En fonction de ce que vous essayez d'accomplir, vous pouvez modifier le résultat final (ce qui est l'une des raisons pour lesquelles vous souhaiterez peut-être le faire).

[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

Gardez à l'esprit: vous ne pouvez pas réaffecter directement le paramètre array

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

cependant (comme indiqué ci-dessous), vous POUVEZ affecter le résultat en modifiant le contenu du tableau:

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

Si vous souhaitez enchaîner les promesses de manière séquentielle avec réduire en utilisant le modèle ci-dessous:

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());

Mais il faut rompre en fonction de ce qui se passe à l'intérieur ou à l'extérieur d'une promesse, les choses deviennent un peu plus compliquées car la boucle de réduction est terminée avant l'exécution de la première promesse, ce qui rend inutile de tronquer le tableau dans les rappels de promesse.

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;
    }
  })
}

Ensuite, vous pouvez faire quelque chose comme ça:

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
luxigo

Une autre implémentation simple que je suis venu avec résoudre le même problème:

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
}
0
alun