如何打破reduce方法的迭代?
的
for (var i = Things.length - 1; i >= 0; i--) {
if(Things[i] <= 0){
break;
}
};
减少
Things.reduce(function(memo, current){
if(current <= 0){
//break ???
//return; <-- this will return undefined to memo, which is not what I want
}
}, 0)
答案 0 :(得分:61)
<强>更新强>
一些评论员提出了一个很好的观点,即原始数组正在被突变,以便在.reduce()
逻辑中提前破解。
因此,在调用后续.slice(0)
步骤之前,我通过添加.reduce()
修改了稍微的答案。
这是通过以线性时间 - O(n)复制其内容来保留原始数组。原始数组也会记录到控制台,以证明它已被保留。
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' ]
<强> OLD 强>
你可以通过改变reduce函数的第四个参数来破坏.reduce()调用的任何迭代:“array”。无需自定义缩减功能。有关.reduce()
参数的完整列表,请参阅Docs。
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
WHY:<?/ em>的
我能想到使用它而不是提供许多其他解决方案的唯一原因是,如果您想要为您的算法维护一种函数式编程方法,并且您希望最具声明性的方法可以实现这一点。如果你的整个目标是将一个数组简化为一个替代的非假名原语(String,Number,Boolean,Symbol),那么我认为这实际上是最好的方法。
为什么不?
有一个完整的参数列表可以使NOT变异函数参数,因为这是一个不好的做法。
答案 1 :(得分:8)
只要您不关心返回值,就可以使用某些和每个等功能。当回调返回false时每个中断,当它返回true时一些:
things.every(function(v, i, o) {
// do stuff
if (timeToBreak) {
return false;
} else {
return true;
}
}, thisArg);
答案 2 :(得分:6)
当然,没有办法让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
答案 3 :(得分:6)
不要使用reduce。只需使用普通迭代器(等等)迭代数组,并在满足条件时突破。
答案 4 :(得分:4)
您可以通过抛出异常来破坏每个代码 - 从而破坏迭代器中的每个构建:
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
}
答案 5 :(得分:2)
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
&#13;
答案 6 :(得分:0)
我解决同一问题的另一个简单实现:
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
}
答案 7 :(得分:0)
由于promise
有resolve
和reject
回调参数,我使用reduce
回调参数创建了break
变通办法函数。它采用与本地reduce
方法相同的所有参数,除了第一个是要处理的数组(避免猴子修补)。第三个[2] initialValue
参数是可选的。请参阅下面的代码段,了解function
reducer。
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);
答案 8 :(得分:0)
如果您要使用以下模式通过reduce顺序链接promise:
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());
但是需要根据承诺内外发生的事情来打破 事情变得有些复杂,因为reduce循环在执行第一个promise之前就终止了,使得在promise回调中截断数组毫无用处,我最终实现了这一点:
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);
});
}
答案 9 :(得分:0)
减少带有中断的功能版本可以实现为“转换”,例如。在下划线。
我尝试使用config标志来实现它以使其停止,以便实现reduce不必更改您当前正在使用的数据结构。
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))
用法2,使用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')
})
用法3,将配置捕获为外部变量
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)
答案 10 :(得分:0)
假设您不需要返回数组,也许您可以使用some()
?
使用 some
代替,它会在您需要时自动中断。向它发送一个 this
累加器。您的测试和累积函数不能是箭头函数,因为它们的 this
在创建箭头函数时已设置。
const array = ['a', 'b', 'c', 'd', 'e'];
var accum = {accum: ''};
function testerAndAccumulator(curr, i, arr){
this.tot += arr[i];
return curr==='c';
};
accum.tot = "";
array.some(testerAndAccumulator, accum);
var result = accum.tot;
在我看来,这是对已接受答案的更好解决方案,前提是您不需要返回数组(例如,在数组运算符链中),因为您不需要更改原始数组,也不需要创建它的副本可能对大型数组不利。
答案 11 :(得分:-1)
你不能从reduce方法内部中断。根据您要完成的任务,您可以更改最终结果(这是您可能想要这样做的一个原因)
[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
请记住:您无法直接重新分配数组参数
[1, 1, 1].reduce( (a, b, c, d) => {
if (c === 0) {
d = [1, 1, 2];
}
return a + b;
}, 0); // still returns 3
但是(如下所述)您可以通过更改数组的内容来影响结果:
[1, 1, 1].reduce( (a, b, c, d) => {
if (c === 0) {
d[2] = 100;
}
return a + b;
}, 0); // now returns 102
答案 12 :(得分:-1)
我以如下方式解决了这个问题,例如在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)