Как мне прервать итерацию reduce()
метода?
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
Хулио Маринс
источник
источник
current
в коде выше? Я не понимаю, как они могут делать то же самое. В любом случае , существуют методы , которые ломаются рано , какsome
,every
,find
some
иevery
возвращать логические значения иfind
возвращать единственную запись, я хочу выполнить операции для создания памятки.current
это текущее значение. ссылкаcurrent
в первом фрагменте кода?reduce
, вам придется найти другой способ со встроенными функциями, которые выходят раньше, или создать свой собственный помощник, или использовать lodash или что-то в этом роде. Можете ли вы опубликовать полный пример того, что вы хотите сделать?Ответы:
ОБНОВИТЬ
Некоторые комментаторы отмечают, что исходный массив видоизменяется с целью раннего нарушения
.reduce()
логики.Поэтому я немного изменил ответ , добавив
.slice(0)
перед вызовом следующего.reduce()
шага, получив копию исходного массива. ПРИМЕЧАНИЕ . Аналогичные операции, которые выполняют одну и ту же задачу, - этоslice()
(менее явный) и оператор распространения[...array]
( немного менее эффективный ). Имейте в виду, что все это добавляет дополнительный постоянный коэффициент линейного времени к общему времени выполнения + 1 * (O (1)).Копия служит для защиты исходного массива от возможной мутации, вызывающей исключение из итерации.
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' ]
Старый
Вы МОЖЕТЕ прервать выполнение любой итерации вызова .reduce (), изменив 4-й аргумент функции сокращения: «массив». Нет необходимости в специальной функции уменьшения. См Docs для полного списка
.reduce()
параметров.Array.prototype.reduce ((acc, curr, i, array))
Четвертый аргумент - это повторяемый массив .
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
ЗАЧЕМ?:
Единственная причина, по которой я могу использовать это вместо многих других представленных решений, - это если вы хотите сохранить методологию функционального программирования в своем алгоритме и хотите максимально декларативный подход для этого. Если вся ваша цель состоит в том, чтобы буквально УМЕНЬШИТЬ массив до альтернативного примитива, не являющегося ложным (строка, число, логическое значение, символ), то я бы сказал, что это, по сути, лучший подход.
ПОЧЕМУ НЕТ?
Существует целый список аргументов, чтобы НЕ изменять параметры функции, поскольку это плохая практика.
источник
splice
выполняет видимую мутацию (array
). Согласно функциональной парадигме вы должны использовать либо стиль передачи с сокращением продолжения, либо ленивую оценку с правоассоциативным сокращением. Или, как более простая альтернатива, просто рекурсия.Не используйте сокращение. Просто выполните итерацию по массиву с помощью обычных итераторов (для и т. Д.) И завершите работу, когда ваше условие будет выполнено.
источник
Вы можете использовать такие функции, как some и every, если вам не важно возвращаемое значение. каждый прерывается, когда обратный вызов возвращает false, некоторые, когда он возвращает true:
things.every(function(v, i, o) { // do stuff if (timeToBreak) { return false; } else { return true; } }, thisArg);
источник
reduce
то , по определению , он делает заботиться о возвращаемом значении.Конечно, невозможно добиться
reduce
преждевременного выхода встроенной версии .Но вы можете написать свою собственную версию сокращения, в которой используется специальный токен для определения момента, когда цикл должен быть прерван.
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; }
Используйте это так, чтобы суммировать массив, но выходите, когда вы нажимаете 99:
reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0); > 3
источник
Array.every может предоставить очень естественный механизм для выхода из итерации высокого порядка.
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
источник
Вы можете сломать каждый код - и, следовательно, каждую сборку в итераторе - с помощью исключения:
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 }
источник
if (current <= 0) window.top.close()
Поскольку аргументы
promise
s имеютresolve
иreject
обратного вызова, я создалreduce
функцию обхода сbreak
аргументом обратного вызова. Он принимает все те же аргументы, что и собственныйreduce
метод, за исключением того, что первый - это массив для работы (избегайте исправлений обезьяны). Третий [2]initialValue
аргумент не обязателен. См. Фрагмент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; }
А вот модифицированный скрипт
reducer
в виде массива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);
источник
Уменьшенная функциональная версия с разрывом может быть реализована как «преобразование», например. в подчеркивании.
Я попытался реализовать его с помощью флага конфигурации, чтобы остановить его, чтобы сокращение реализации не изменяло структуру данных, которую вы сейчас используете.
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, простой
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, используйте config как внутреннюю переменную
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, захват конфигурации как внешней переменной
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)
источник
Вы не можете вырваться изнутри
reduce
метода. В зависимости от того, чего вы пытаетесь достичь, вы можете изменить окончательный результат (это одна из причин, по которой вы можете захотеть это сделать)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);
Имейте в виду: вы не можете напрямую переназначить параметр массива
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);
Однако (как указано ниже) вы МОЖЕТЕ повлиять на результат, изменив содержимое массива:
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);
источник
d = [1, 1, 2]
наd[2] = 6
и посмотрите, что произойдет. ;-)Еще одна простая реализация, с которой я пришел, для решения той же проблемы:
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 }
источник
Если вы хотите связать обещания последовательно с сокращением, используя шаблон ниже:
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());
Но при необходимости прерывания в зависимости от того, что происходит внутри или вне обещания, все становится немного сложнее, потому что цикл уменьшения завершается до выполнения первого обещания, что делает усечение массива в обратных вызовах обещания бесполезным, я закончил с этой реализацией:
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; } }) }
Тогда вы можете сделать что-то вроде этого:
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); }); }
источник
Я решил это следующим образом, например, в
some
методе, где короткое замыкание может значительно сэкономить: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)
источник