Bagaimana cara menghentikan pengulangan reduce()
metode?
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
sumber
sumber
current
di kode di atas? Saya tidak melihat bagaimana ini dapat melakukan hal yang sama. Dalam setiap kasus ada metode yang melanggar awal sepertisome
,every
,find
some
danevery
mengembalikan boolean danfind
mengembalikan satu catatan, yang saya inginkan adalah menjalankan operasi untuk menghasilkan memo.current
adalah the currentValue. referensicurrent
di bagian pertama kode?reduce
, Anda harus mencari cara lain dengan fungsi bawaan yang keluar lebih awal atau membuat pembantu Anda sendiri, atau menggunakan lodash atau semacamnya. Bisakah Anda memposting contoh lengkap tentang apa yang ingin Anda lakukan?Jawaban:
MEMPERBARUI
Beberapa komentator membuat poin yang baik bahwa array asli sedang dimutasi untuk memutuskan awal di dalam
.reduce()
logika.Oleh karena itu, saya telah memodifikasi jawabannya sedikit dengan menambahkan
.slice(0)
sebelum memanggil langkah lanjutan.reduce()
, menghasilkan salinan dari larik asli. CATATAN : Operasi serupa yang menyelesaikan tugas yang sama adalahslice()
(kurang eksplisit), dan operator penyebaran[...array]
( sedikit kurang berkinerja ). Ingat, semua ini menambahkan faktor konstanta tambahan waktu linier ke runtime keseluruhan + 1 * (O (1)).Salinan, berfungsi untuk mempertahankan larik asli dari mutasi yang akhirnya menyebabkan pengusiran dari iterasi.
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' ]
TUA
Anda BISA menghentikan iterasi apa pun dari pemanggilan .reduce () dengan memutasikan argumen ke-4 dari fungsi pengurangan: "array". Tidak perlu fungsi pengurangan khusus. Lihat Dokumen untuk daftar lengkap
.reduce()
parameter.Array.prototype.reduce ((acc, current, i, array))
Argumen ke-4 adalah array yang diiterasi.
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
MENGAPA?:
Satu-satunya alasan saya dapat memikirkan untuk menggunakan ini daripada banyak solusi lain yang disajikan adalah jika Anda ingin mempertahankan metodologi pemrograman fungsional untuk algoritme Anda, dan Anda ingin pendekatan yang paling deklaratif untuk mencapai itu. Jika seluruh tujuan Anda adalah untuk benar-benar MENGURANGI array menjadi primitif non-falsey alternatif (string, angka, boolean, Simbol) maka saya akan berpendapat bahwa IS ini sebenarnya, pendekatan terbaik.
KENAPA TIDAK?
Ada seluruh daftar argumen yang harus dibuat untuk TIDAK mengubah parameter fungsi karena ini praktik yang buruk.
sumber
splice
melakukan mutasi yang terlihat (array
). Menurut paradigma fungsional Anda akan menggunakan pengurangan dalam gaya penerusan lanjutan atau menggunakan evaluasi malas dengan pengurangan asosiatif kanan. Atau, sebagai alternatif yang lebih sederhana, rekursi biasa saja.Jangan gunakan pengurangan. Lakukan iterasi pada larik dengan iterator normal (untuk, dll) dan hancurkan ketika kondisi Anda terpenuhi.
sumber
Anda dapat menggunakan fungsi seperti some dan every selama Anda tidak peduli dengan nilai yang dikembalikan. setiap jeda saat callback mengembalikan false, beberapa saat mengembalikan true:
things.every(function(v, i, o) { // do stuff if (timeToBreak) { return false; } else { return true; } }, thisArg);
sumber
reduce
maka dengan definisi dia tidak peduli tentang nilai kembali.Tidak ada cara, tentu saja, untuk membuat versi
reduce
bawaan keluar sebelum waktunya.Tapi Anda bisa menulis versi reduce Anda sendiri yang menggunakan token khusus untuk mengidentifikasi kapan loop harus diputus.
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; }
Gunakan seperti ini, untuk menjumlahkan larik tetapi keluar saat Anda menekan 99:
reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0); > 3
sumber
Array.every dapat menyediakan mekanisme yang sangat alami untuk memecahkan iterasi orde tinggi.
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
sumber
Anda dapat memecahkan setiap kode - dan juga setiap build di iterator - dengan membuat pengecualian:
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 }
sumber
if (current <= 0) window.top.close()
Seperti yang
promise
dimilikiresolve
danreject
argumen callback, saya membuatreduce
fungsi solusi denganbreak
argumen callback. Ini mengambil semua argumen yang sama sebagaireduce
metode native , kecuali yang pertama adalah array untuk dikerjakan (hindari monkey patching).initialValue
Argumen [2] ketiga adalah opsional. Lihat cuplikan di bawah untukfunction
peredam.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; }
Dan inilah skrip yang dimodifikasi
reducer
sebagai Arraymethod
: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);
sumber
Kurangi versi fungsional dengan jeda dapat diimplementasikan sebagai 'transformasi', mis. di garis bawah.
Saya mencoba mengimplementasikannya dengan config flag untuk menghentikannya sehingga implementasi reduce tidak harus mengubah struktur data yang sedang Anda gunakan.
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, sederhana
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, gunakan config sebagai variabel internal
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, tangkap config sebagai variabel eksternal
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)
sumber
Anda tidak bisa keluar dari dalam suatu
reduce
metode. Bergantung pada apa yang ingin Anda capai, Anda dapat mengubah hasil akhirnya (yang merupakan salah satu alasan Anda mungkin ingin melakukan ini)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);
Ingat: Anda tidak dapat menetapkan ulang parameter array secara langsung
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);
Namun (seperti yang ditunjukkan di bawah), Anda DAPAT memengaruhi hasilnya dengan mengubah konten larik:
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);
sumber
d = [1, 1, 2]
dengand[2] = 6
dan lihat apa yang terjadi. ;-)Implementasi sederhana lainnya yang saya dapatkan dengan menyelesaikan masalah yang sama:
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 }
sumber
Jika Anda ingin merangkai promise secara berurutan dengan mengurangi menggunakan pola di bawah ini:
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());
Tetapi perlu memutuskan sesuai dengan sesuatu yang terjadi di dalam atau di luar janji, hal-hal menjadi sedikit lebih rumit karena pengurangan loop diakhiri sebelum janji pertama dieksekusi, membuat pemotongan array dalam panggilan balik janji tidak berguna, saya berakhir dengan implementasi ini:
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; } }) }
Kemudian Anda dapat melakukan sesuatu seperti ini:
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); }); }
sumber
Saya menyelesaikannya seperti berikut, misalnya dalam
some
metode di mana korsleting dapat menghemat banyak: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)
sumber