Como posso interromper a iteração do 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
fonte
fonte
current
no código acima? Não vejo como eles podem fazer a mesma coisa. Em qualquer caso, existem métodos que quebram cedosome
, comoevery
,find
some
eevery
retornar booleanos efind
retornar um único registro, o que eu quero é executar operações para gerar um memo.current
é o valor atual. referênciacurrent
na primeira parte do código?reduce
, você terá que encontrar outra maneira com funções embutidas que saem mais cedo ou criam seu próprio ajudante, ou usam lodash ou algo assim. Você pode postar um exemplo completo do que deseja fazer?Respostas:
ATUALIZAR
Alguns dos comentaristas afirmam que o array original está sofrendo mutação para quebrar logo no início da
.reduce()
lógica.Portanto, modifiquei ligeiramente a resposta adicionando um
.slice(0)
antes de chamar uma.reduce()
etapa de acompanhamento , produzindo uma cópia do array original. NOTA : Operações semelhantes que realizam a mesma tarefa sãoslice()
(menos explícitas) e operador de propagação[...array]
( desempenho ligeiramente inferior ). Lembre-se de que tudo isso adiciona um fator constante adicional de tempo linear ao tempo de execução geral + 1 * (O (1)).A cópia serve para preservar a matriz original da eventual mutação que causa a ejeção da iteração.
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' ]
VELHO
Você PODE interromper qualquer iteração de uma invocação .reduce () alterando o 4º argumento da função de redução: "array". Não há necessidade de uma função de redução personalizada. Veja Docs para uma lista completa de
.reduce()
parâmetros.Array.prototype.reduce ((acc, curr, i, array))
O quarto argumento é a matriz que está sendo iterada.
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
PORQUE?:
A única razão pela qual posso pensar em usar isso em vez de muitas outras soluções apresentadas é se você deseja manter uma metodologia de programação funcional para seu algoritmo e deseja a abordagem mais declarativa possível para fazer isso. Se todo o seu objetivo é literalmente REDUZIR um array a um primitivo não falsey alternativo (string, número, booleano, símbolo), então eu diria que isso é, de fato, a melhor abordagem.
POR QUE NÃO?
Há uma lista completa de argumentos para NÃO alterar os parâmetros da função, pois é uma prática ruim.
fonte
splice
realiza uma mutação visível (array
). De acordo com o paradigma funcional, você usaria uma redução no estilo de aprovação de continuação ou utilizaria a avaliação preguiçosa com uma redução associativa à direita. Ou, como uma alternativa mais simples, simplesmente recursão.Não use reduzir. Apenas itere na matriz com iteradores normais (para, etc) e interrompa quando sua condição for atendida.
fonte
Você pode usar funções como some e every , desde que não se importe com o valor de retorno. todos são interrompidos quando o retorno de chamada retorna falso, alguns quando retorna verdadeiro:
things.every(function(v, i, o) { // do stuff if (timeToBreak) { return false; } else { return true; } }, thisArg);
fonte
reduce
isso, por definição, ele se preocupa com o valor de retorno.Obviamente, não há como obter a versão integrada do
reduce
saia prematuramente.Mas você pode escrever sua própria versão de reduzir, que usa um token especial para identificar quando o loop deve ser interrompido.
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-o assim, para somar uma matriz, mas saia quando chegar a 99:
reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0); > 3
fonte
Array.every pode fornecer um mecanismo muito natural para interromper a iteração de alta ordem.
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
fonte
Você pode quebrar cada código - e, portanto, cada compilação no iterador - lançando uma exceção:
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 }
fonte
if (current <= 0) window.top.close()
Como os
promise
têmresolve
e osreject
argumentos de retorno de chamada, criei areduce
função de solução alternativa com obreak
argumento de retorno de chamada. Leva os mesmos argumentos que oreduce
método nativo , exceto o primeiro é um array para trabalhar (evite o patching do macaco). O terceiro [2]initialValue
argumento é opcional. Veja o snippet abaixo para ofunction
redutor.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; }
E aqui está o script
reducer
como um Arraymethod
modificado: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);
fonte
A versão funcional de redução com quebra pode ser implementada como 'transformar', ex. em sublinhado.
Tentei implementá-lo com um sinalizador de configuração para interrompê-lo, de modo que a redução da implementação não precise alterar a estrutura de dados que você está usando no momento.
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
Uso1, simples
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 variável 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 configuração como variável 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)
fonte
Você não pode quebrar de dentro de um
reduce
método. Dependendo do que você está tentando realizar, você pode alterar o resultado final (que é uma razão pela qual você pode querer fazer isso)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);
Lembre-se: você não pode reatribuir o parâmetro de matriz diretamente
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);
No entanto (conforme indicado abaixo), você PODE afetar o resultado alterando o conteúdo da 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);
fonte
d = [1, 1, 2]
pord[2] = 6
e veja o que acontece. ;-)Outra implementação simples que recebi para resolver o mesmo 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 }
fonte
Se você deseja encadear promessas sequencialmente com redução usando o padrão abaixo:
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());
Mas precisa quebrar de acordo com algo acontecendo dentro ou fora de uma promessa, as coisas se tornam um pouco mais complicadas porque o loop de redução é encerrado antes que a primeira promessa seja executada, tornando inútil o truncamento da matriz nos retornos de chamada da promessa. Acabei com esta implementação:
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; } }) }
Então você pode fazer algo assim:
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); }); }
fonte
Resolvi da seguinte maneira, por exemplo, no
some
método em que um curto-circuito pode economizar muito: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)
fonte