¿Cómo puedo romper la iteración del reduce()
método?
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)
javascript
loops
reduce
Julio Marins
fuente
fuente
current
en el código de arriba? No veo cómo estos pueden hacer lo mismo. En cualquier caso, hay métodos que rompen principios comosome
,every
,find
some
yevery
devolver booleanos yfind
devolver un solo registro, lo que quiero es ejecutar operaciones para generar una nota.current
es el valor actual. referenciacurrent
en la primera parte del código?reduce
, tendrá que encontrar otra forma con funciones integradas que salgan temprano o creen su propio ayudante, o usen lodash o algo así. ¿Puedes publicar un ejemplo completo de lo que quieres hacer?Respuestas:
ACTUALIZAR
Algunos de los comentaristas hacen un buen punto de que la matriz original está siendo mutada para romper temprano dentro de la
.reduce()
lógica.Por lo tanto, modifiqué ligeramente la respuesta agregando un
.slice(0)
antes de llamar a un.reduce()
paso de seguimiento , obteniendo una copia de la matriz original. NOTA : Operaciones similares que logran la misma tarea sonslice()
(menos explícitas) y operador de propagación[...array]
(un poco menos de rendimiento ). Tenga en cuenta que todos estos agregan un factor constante adicional de tiempo lineal al tiempo de ejecución general + 1 * (O (1)).La copia sirve para preservar la matriz original de la eventual mutación que causa la expulsión de la iteración.
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' ]
ANTIGUO
PUEDE interrumpir en cualquier iteración de una invocación .reduce () mutando el cuarto argumento de la función de reducción: "matriz". No se necesita una función de reducción personalizada. Consulte Documentos para obtener una lista completa de
.reduce()
parámetros.Array.prototype.reduce ((acc, curr, i, array))
El cuarto argumento es la matriz sobre la que se itera.
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
¿POR QUÉ?:
La única razón por la que puedo pensar en usar esto en lugar de las muchas otras soluciones presentadas es si desea mantener una metodología de programación funcional para su algoritmo y desea el enfoque más declarativo posible para lograrlo. Si todo su objetivo es REDUCIR literalmente una matriz a una primitiva alternativa que no sea falsey (cadena, número, booleano, símbolo), entonces yo diría que esto ES, de hecho, el mejor enfoque.
¿POR QUÉ NO?
Hay una lista completa de argumentos para NO modificar los parámetros de la función, ya que es una mala práctica.
fuente
splice
realiza una mutación visible (array
). De acuerdo con el paradigma funcional, usaría una reducción en el estilo de aprobación de continuación o utilizaría una evaluación perezosa con una reducción asociativa a la derecha. O, como alternativa más simple, simplemente recursividad.No use reducir. Simplemente repita la matriz con iteradores normales (para, etc.) y salga cuando se cumpla su condición.
fuente
Puede utilizar funciones como cierta y cada todo el tiempo que no se preocupan por el valor de retorno. todos se rompen cuando la devolución de llamada devuelve falso, algunos cuando devuelve verdadero:
things.every(function(v, i, o) { // do stuff if (timeToBreak) { return false; } else { return true; } }, thisArg);
fuente
reduce
entonces, por definición, que no se preocupan por el valor de retorno.Por supuesto, no hay forma de que la versión incorporada de
reduce
salga antes de tiempo.Pero puede escribir su propia versión de reduce, que usa un token especial para identificar cuándo debe romperse el bucle.
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; }
Úselo así, para sumar una matriz pero salga cuando llegue a 99:
reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0); > 3
fuente
Array.every puede proporcionar un mecanismo muy natural para romper la iteración de alto orden.
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
fuente
Puede romper cada código, y por lo tanto cada compilación en el iterador, lanzando una excepción:
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 }
fuente
if (current <= 0) window.top.close()
Como los argumentos
promise
tienenresolve
y dereject
devolución de llamada, creé lareduce
función de solución alternativa con elbreak
argumento de devolución de llamada. Toma todos los mismos argumentos que elreduce
método nativo , excepto que el primero es una matriz en la que trabajar (evite parches de mono). El tercerinitialValue
argumento [2] es opcional. Consulte el fragmento a continuación para ver elfunction
reductor.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; }
Y aquí está el script modificado
reducer
como una matrizmethod
: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);
fuente
Reducir la versión funcional con ruptura se puede implementar como 'transformar', ej. en subrayado.
Intenté implementarlo con un indicador de configuración para detenerlo, de modo que la implementación reduce no tenga que cambiar la estructura de datos que está utilizando actualmente.
const transform = (arr, reduce, init, config = {}) => { const result = arr.reduce((acc, item, i, arr) => { if (acc.found) return acc acc.value = reduce(config, acc.value, item, i, arr) if (config.stop) { acc.found = true } return acc }, { value: init, found: false }) return result.value } module.exports = transform
Usage1, simple
const a = [0, 1, 1, 3, 1] console.log(transform(a, (config, acc, v) => { if (v === 3) { config.stop = true } if (v === 1) return ++acc return acc }, 0))
Usage2, use config como variable interna
const pixes = Array(size).fill(0) const pixProcessed = pixes.map((_, pixId) => { return transform(pics, (config, _, pic) => { if (pic[pixId] !== '2') config.stop = true return pic[pixId] }, '0') })
Usage3, captura de configuración como variable externa
const thrusts2 = permute([9, 8, 7, 6, 5]).map(signals => { const datas = new Array(5).fill(_data()) const ps = new Array(5).fill(0) let thrust = 0, config do { config = {} thrust = transform(signals, (_config, acc, signal, i) => { const res = intcode( datas[i], signal, { once: true, i: ps[i], prev: acc } ) if (res) { [ps[i], acc] = res } else { _config.stop = true } return acc }, thrust, config) } while (!config.stop) return thrust }, 0)
fuente
No se puede romper desde el interior de un
reduce
método. Dependiendo de lo que esté tratando de lograr, podría alterar el resultado final (que es una de las razones por las que puede querer hacer esto)const result = [1, 1, 1].reduce((a, b) => a + b, 0); // returns 3 console.log(result);
const result = [1, 1, 1].reduce((a, b, c, d) => { if (c === 1 && b < 3) { return a + b + 1; } return a + b; }, 0); // now returns 4 console.log(result);
Tenga en cuenta: no puede reasignar el parámetro de matriz directamente
const result = [1, 1, 1].reduce( (a, b, c, d) => { if (c === 0) { d = [1, 1, 2]; } return a + b; }, 0); // still returns 3 console.log(result);
Sin embargo (como se señala a continuación), PUEDE afectar el resultado cambiando el contenido de la matriz:
const result = [1, 1, 1].reduce( (a, b, c, d) => { if (c === 0) { d[2] = 100; } return a + b; }, 0); // now returns 102 console.log(result);
fuente
d = [1, 1, 2]
cond[2] = 6
y ver qué pasa. ;-)Otra implementación simple con la que vine resolviendo el mismo problema:
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 }
fuente
Si desea encadenar promesas secuencialmente con reducir utilizando el patrón a continuación:
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());
Pero si es necesario romper de acuerdo con algo que sucede dentro o fuera de una promesa, las cosas se vuelven un poco más complicadas porque el ciclo de reducción se termina antes de que se ejecute la primera promesa, lo que hace que truncar la matriz en las devoluciones de llamada de promesa sea inútil, terminé con esta implementación:
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; } }) }
Entonces puedes hacer algo como esto:
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); }); }
fuente
Lo resolví de la siguiente manera, por ejemplo, en el
some
método donde el cortocircuito puede ahorrar mucho: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)
fuente