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)
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.
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);
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
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.
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
}
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
Comme les promise
s 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);
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
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);
});
}
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
}