How to get all the array indexes that meet a condition in Javascript? - javascript

I would like to create a method that works like Array.prototype.findIndex but that returns all the indexes that match the condition:
Array.prototype.which = function(condition) {
let result = this.reduce((acc, x, i, arr) => {
if (condition) { acc.push(i) }
return acc
}, [])
return result
}
So that I could do:
[ 'a', null, 'b' ].which(x => x !== null) // [0, 2]
This doesn't work because I don't know how to link the x argument in the function call with the x value inside the which function.

You need to call the predicate:
Array.prototype.which = function(predicate) {
let result = this.reduce((acc, x, i, arr) => {
if (predicate(x)) { acc.push(i) }
return acc
}, [])
return result
}

I'd prefer Array#forEach.
Array.prototype.which = function(condition) {
let result = [];
this.forEach((v,i) => condition(v) ? result.push(i) : null);
return result;
}
console.log([1,2,3,4,5,6].which(v => v > 3));

An old-school for-loop method:
Array.prototype.which = function(test) {
const result = [];
for(let i = 0, l = this.length; i < l; i++) {
if (test(this[i])) {
result.push(i);
}
}
return result;
}
While it is easier to make a mistake with for and while, they sometimes offer performance gain because of fewer function calls.

Related

how to make _.includes method

I am a junior developer who has been coding for 4 weeks.
I'm working on a JavaScript method.
I'll show you the code I used first.
_.each = function (collection, iteratee) {
if(Array.isArray(collection)===true){
for(let i=0;i<collection.length;i++){
iteratee(collection[i],i,collection)
}
}else{
let objvalues= Object.values(collection)
let objkeys = Object.keys(collection)
for(let i=0;i<objvalues.length;i++){
iteratee(objvalues[i],objkeys[i],collection)
}
}
};
_.includes = function (arr, target) {
let result
_.each(arr, function(a){
if(a === target)
result = true
if (a !== target)
result = false
})
return result;
};
It's a condition.
If the _.include method matches the value found by the element in the array, the true must be returned.
If the element in the array does not match the value you are looking for, you must return false.
I made the _include method.
If the element in the array does not match the value you are looking for, the return to false is successful.ten thousand
If the element in the array matches the value you are looking for, you must return true
This is where you fail.
It seems that the ture cannot be returned and only false is returned.
How should I handle this?
The problem is here:
_.each(arr, function(a){
if(a === target)
result = true
if (a !== target)
result = false
})
You reassign result on every iteration. As a result, the only iteration that matters for the final value of result is the last iteration.
Instead, initialize result to false, and reassign to true when the target is found:
const _ = {};
_.each = function(collection, iteratee) {
if (Array.isArray(collection) === true) {
for (let i = 0; i < collection.length; i++) {
iteratee(collection[i], i, collection)
}
} else {
let objvalues = Object.values(collection)
let objkeys = Object.keys(collection)
for (let i = 0; i < objvalues.length; i++) {
iteratee(objvalues[i], objkeys[i], collection)
}
}
};
_.includes = function(arr, target) {
let result = false;
_.each(arr, function(a) {
if (a === target)
result = true
})
return result;
};
console.log(
_.includes([1, 2, 3], 2)
);
It'd be cleaner to break the loop once a match is found, but your _each isn't set up for that:
const _ = {};
_.each = function(collection, iteratee) {
if (Array.isArray(collection) === true) {
for (let i = 0; i < collection.length; i++) {
iteratee(collection[i], i, collection)
}
} else {
let objvalues = Object.values(collection)
let objkeys = Object.keys(collection)
for (let i = 0; i < objvalues.length; i++) {
iteratee(objvalues[i], objkeys[i], collection)
}
}
};
_.includes = function(arr, target) {
for (const a of arr) {
if (a === target)
return true
}
return false;
};
console.log(
_.includes([1, 2, 3], 2)
);
In addition to what CertainPerfomance already said, you could see how Array.includes was implemented to get an inspiration, here is the specifications from TC39.

Recursive function can't read object property

I made a function that turns an array into a list which is like this:
function arrayToList(array) {
var result = null;
for (var i = array.length; i >= 0; i--) {
result = {
value: array[i],
rest: result
}
}
return result
}
console.log(arrayToList([10, 20]))
And then I tried to make a recursive function that takes a list and a number and returns an element at a given position in the list
Here's the first attempt:
function arrayToList(array) {
var result = null;
for (var i = array.length; i >= 0; i--) {
result = {
value: array[i],
rest: result
}
}
return result
}
function nthRecur(list, index) {
var counter = 0
if (counter === index) {
return list.value
} else {
counter++
list = list.rest
return nthRecur(list, index)
}
}
console.log(nthRecur(arrayToList([10, 20, 30]), 1))
Second attempt:
function arrayToList(array) {
var result = null;
for (var i = array.length; i >= 0; i--) {
result = {
value: array[i],
rest: result
}
}
return result
}
function nthRecur(list, index) {
var counter = 0
if (counter === index) {
return list.value
} else {
list = list.rest
return nthRecur(list, index - 1)
}
}
console.log(nthRecur(arrayToList([10, 20, 30]), 1))
It just worked. I don't know why. Can someone explain?
The problem is this counter is local variable and in each recursive call its set to 0. You need to create a wrapper function and declare counter in it.
var counter = 0;
function arrayToList(array)
{
var result = null;
for (var i = array.length; i >= 0; i--)
{
result = {
value: array[i],
rest: result
}
}
return result
}
function nthRecur(list, index)
{
let counter = 0;
function helper(list){
if(counter === index)
{
return list.value
}
else
{
counter++
list = list.rest
return helper(list)
}
}
return helper(list);
}
console.log(nthRecur(arrayToList([10, 20, 30]), 1));
You could shorten it by using the index directly.
function arrayToList(array) {
var result = null,
i = array.length;
while (i--) {
result = { value: array[i], rest: result };
}
return result;
}
function nthRecur(list, index) {
return index
? nthRecur(list.rest, index - 1)
: list.value;
}
var list = arrayToList([10, 20, 30]);
console.log(list);
console.log(nthRecur(list, 1));
To improve on Nina Scholz's answer, you can add an additional check to each recursion to prevent out of bounds indices from throwing an error:
function arrayToList (array) {
return array.reduceRight(
(rest, value) => ({ value, rest }),
null
);
}
function item (list, index) {
return list
? index
? item(list.rest, index - 1)
: list.value
: undefined;
}
const list = arrayToList([10, 20, 30]);
console.log(item(list, 0));
console.log(item(list, 1));
console.log(item(list, 2));
console.log(item(list, 3));
If you want to support negative indices as well, similar to slice() and splice(), you'll need another function to get the length of the list and then adjust the item function accordingly:
function arrayToList (array) {
return array.reduceRight(
(rest, value) => ({ value, rest }),
null
);
}
function length (list, index = 0) {
return list
? length(list.rest, index + 1)
: index;
}
function item (list, index) {
return list
? index
? index > 0 || (index += length(list)) > 0
? item(list.rest, index - 1)
: index < 0
? undefined
: list.value
: list.value
: undefined;
}
const list = arrayToList([10, 20, 30]);
console.log(item(list, 0));
console.log(item(list, 1));
console.log(item(list, 2));
console.log(item(list, 3));
console.log(item(list, -1));
console.log(item(list, -2));
console.log(item(list, -3));
console.log(item(list, -4));
P.S. arrayToList() above uses reduceRight() to simplify the implementation. listToArray() can use spread syntax and recursion to simplify its implementation:
function arrayToList (array) {
return array.reduceRight(
(rest, value) => ({ value, rest }),
null
);
}
function listToArray (list, array = []) {
return list
? listToArray(list.rest, [...array, list.value])
: array;
}
const list = arrayToList([10, 20, 30]);
const array = listToArray(list);
console.log(list);
console.log(array);

Create an Array recursively

I am trying to implement a recursive Function which returns an array of functions by filling it recursively
//arr = [1..1000]
//arr2 = [1,2,3]
function divideToSmallerTasks(arr, arr2) {
let arrLength = arr.length;
if (arr === undefined || arrLength == 0) {
return [];
} else if (arrLength > 100) {
return (getRelatedGames(arr.slice(0, 100), arr2)).push(divideToSmallerTasks(arr.slice(100, arrLength), arr2));
} else {
return (getRelatedGames(arr, arr2).push(divideToSmallerTasks([], arr2));
}
I expected to get back an array of functions smallerTasks = [function(arr[1..100],arr2[1,2,3]),function(arr[100,..200],arr2[1,2,3]),...] so I can run them in parallel later.
Your code is invoking the getRelatedGames function immediately as part of divideToSmallerTasks. What you want to do is create an anonymous function which, when executed, will run getRelatedGames as you stated, and return that. Something closer to the following: (untested)
function divideToSmallerTasks(arr,arr2){
let arrLength = arr.length;
if(arr === undefined || arrLength == 0){
return [];
}else if(arrLength > 100) {
let result = divideToSmallerTasks(arr.slice(100,arrLength),arr2);
result.push(function () { return getRelatedGames(arr,arr2); });
return result;
}else{
return [function () { return getRelatedGames(arr,arr2); }]
;}
This should result in a list of functions that you can execute one by one whenever you wish and perform the expected getRelatedGames call.
You need to return an array, instead of a new length, which is returned by push.
If you do not get a closure, you could add a function which calls getRelatedGames later. Here, this function is replaced with console.log.
function divideToSmallerTasks(arr, arr2) {
const defer = (fn, ...args) => () => fn(...args);
let arrLength = arr.length;
if (arr === undefined || !arrLength) {
return [];
}
if (arrLength > 3) {
return [defer(console.log, arr.slice(0, 3), arr2), ...divideToSmallerTasks(arr.slice(3, arrLength), arr2)];
}
return [defer(console.log, arr, arr2)];
}
var array = divideToSmallerTasks([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ['a', 'b', 'c']);
console.log(array);
array.forEach(fn => fn());
.as-console-wrapper { max-height: 100% !important; top: 0; }

Turning for loop into forEach JavaScript

Working on a function that verifies if each number in an array is true or false and returns the first true number.
Have a working solution with a for loop as follows:
function findElement(arr, func) {
var num;
for (var a = 0; a < arr.length; a++) {
if (func(arr[a])) {
num = arr[a];
return num;
}
}
return num;
}
findElement([1, 3, 5, 8, 9, 10], function(num) {
return num % 2 === 0;
})
// Should return 8
But I'm trying (in order to get my head around forEach better) to convert it into a forEach loop.
This is where I am so far, but I don't see how to actually return the num out of the loop after it's been established that the function result is true:
function findElement(arr, func) {
if (arr.forEach(func) === true) {
return num;
}
}
findElement([1, 2, 3, 4], num => num % 2 === 0);
Not sure how to use forEach but you could use array.prototype.filter:
function findElement(arr, func) {
return arr.filter(func)[0];
}
#cdhowie suggested the use array.prototype.find in order to need the usage of [0].
function findElement(arr, func) {
return arr.find(func);
}
Obviously, that is just a guess because it may raise an error if no item in the array meets the requirements of func.
If you still are looking about use forEach maybe you could do something like this:
function findElement(arr, func) {
matches = []
arr.forEach((item) => {
if (func(item)) {
matches.push(item)
}
});
return matches[0];
}
Or:
function findElement(arr, func) {
match = null
arr.forEach((item) => {
match = (match == null && func(item)) ? item : match
});
return match;
}
Again, you will have to check how to handle the error if no item in the array meets the requirements f your func.
Any of both codes produce
console.log(findElement([1, 3, 5, 8, 9, 10], function(num) { return num % 2 === 0; })))
8
function findElement(arr, func) {
var num;
arr.forEach(function(element) {
if (!num && func(element)) {
num = element;
}
});
return num;
}
For more options you can check this question: how to stop Javascript forEach?
As has already been mentioned, this is not how forEach should be used.
However, you can still get the behavior you want:
function findElement(arr, func) {
var num;
arr.forEach(item =>{
if (func(item)) {
num = item;
// .forEach does not have an early return
// but you can force it to skip elements by removing them
while (true) {
// Remove all elements
var removedItem = arr.shift();
if (removedItem === undefined) {
// All elements removed
break;
}
}
}
return num;
}
This is even mentioned in the documentation
maybe like this
function findElement(arr, func) {
var num = null;
for (var a = 0; a < arr.length && num === null; a++) {
var val = arr[a];
num = func(val) ? val : null;
}
return num;
}
console.log(findElement([1, 3, 5, 8, 9, 10], function(num) {
return num % 2 === 0;
}));
here is your findElement method with foreach
function findElement(arr, func) {
var num = 0;
arr.forEach(function(item){
if (func(item))
return item;
})
return num;
}

How to early break reduce() method?

How can I break the iteration of reduce() method?
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)
You CAN break on any iteration of a .reduce() invocation by mutating the 4th argument of the reduce function: "array". No need for a custom reduce function. See Docs for full list of .reduce() parameters.
Array.prototype.reduce((acc, curr, i, array))
The 4th argument is the array being iterated over.
const array = ['apple', '-pen', '-pineapple', '-pen'];
const x = array
.reduce((acc, curr, i, arr) => {
if(i === 2) arr.splice(1); // eject early
return acc += curr;
}, '');
console.log('x: ', x); // x: apple-pen-pineapple
WHY?:
The one and only reason I can think of to use this instead of the many other solutions presented is if you want to maintain a functional programming methodology to your algorithm, and you want the most declarative approach possible to accomplish that. If your entire goal is to literally REDUCE an array to an alternate non-falsey primitive (string, number, boolean, Symbol) then I would argue this IS in fact, the best approach.
WHY NOT?
There's a whole list of arguments to make for NOT mutating function parameters as it's a bad practice.
UPDATE
Some of the commentators make a good point that the original array is being mutated in order to break early inside the .reduce() logic.
Therefore, I've modified the answer slightly by adding a .slice(0) before calling a follow-on .reduce() step, yielding a copy of the original array.
NOTE: Similar ops that accomplish the same task are slice() (less explicit), and spread operator [...array] (slightly less performant). Bear in mind, all of these add an additional constant factor of linear time to the overall runtime ... + O(n).
The copy, serves to preserve the original array from the eventual mutation that causes ejection from iteration.
const array = ['apple', '-pen', '-pineapple', '-pen'];
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: apple-pen-pineapple
// original Arr: ['apple', '-pen', '-pineapple', '-pen']
Don't use reduce. Just iterate on the array with normal iterators (for, etc) and break out when your condition is met.
You can use functions like some and every as long as you don't care about the return value. every breaks when the callback returns false, some when it returns true:
things.every(function(v, i, o) {
// do stuff
if (timeToBreak) {
return false;
} else {
return true;
}
}, thisArg);
Edit
A couple of comments that "this doesn't do what reduce does", which is true, but it can. Here's an example of using every in a similar manner to reduce that returns as soon as the break condition is reached.
// Soruce data
let data = [0,1,2,3,4,5,6,7,8];
// Multiple values up to 5 by 6,
// create a new array and stop processing once
// 5 is reached
let result = [];
data.every(a => a < 5? result.push(a*6) : false);
console.log(result);
This works because the return value from push is the length of the result array after the new element has been pushed, which will always be 1 or greater (hence true), otherwise it returns false and the loop stops.
There is no way, of course, to get the built-in version of reduce to exit prematurely.
But you can write your own version of reduce which uses a special token to identify when the loop should be broken.
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 it like this, to sum an array but exit when you hit 99:
reduce([1, 2, 99, 3], (a, b) => b === 99 ? EXIT_REDUCE : a + b, 0);
> 3
Array.every can provide a very natural mechanism for breaking out of high order iteration.
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
You can break every code - and thus every build in iterator - by throwing an exception:
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
}
You can use try...catch to exit the loop.
try {
Things.reduce(function(memo, current){
if(current <= 0){
throw 'exit loop'
//break ???
//return; <-- this will return undefined to memo, which is not what I want
}
}, 0)
} catch {
// handle logic
}
As the promises have resolve and reject callback arguments, I created the reduce workaround function with the break callback argument. It takes all the same arguments as native reduce method, except the first one is an array to work on (avoid monkey patching). The third [2] initialValue argument is optional. See the snippet below for the 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;
}
And here is the reducer as an Array method modified script:
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);
Reduce functional version with break can be implemented as 'transform', ex. in underscore.
I tried to implement it with a config flag to stop it so that the implementation reduce doesn't have to change the data structure that you are currently using.
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 one
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 as internal variable
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, capture config as external variable
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)
You cannot break from inside of a reduce method. Depending on what you are trying to accomplish you could alter the final result (which is one reason you may want to do this)
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);
Keep in mind: you cannot reassign the array parameter directly
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);
However (as pointed out below), you CAN affect the outcome by changing the array's contents:
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);
Providing you do not need to return an array, perhaps you could use some()?
Use some instead which auto-breaks when you want. Send it a this accumulator. Your test and accumulate function cannot be an arrow function as their this is set when the arrow function is created.
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;
In my opinion this is the better solution to the accepted answer provided you do not need to return an array (eg in a chain of array operators), as you do not alter the original array and you do not need to make a copy of it which could be bad for large arrays.
So, to terminate even earlier the idiom to use would be arr.splice(0).
Which prompts the question, why can't one just use arr = [] in this case?
I tried it and the reduce ignored the assignment, continuing on unchanged.
The reduce idiom appears to respond to forms such as splice but not forms such as the assignment operator??? - completely unintuitive - and has to be rote-learnt as precepts within the functional programming credo ...
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
The problem is, that inside of the accumulator it is not possible to just stop the whole process. So by design something in the outer scope must be manipulated, which always leads to a necessary mutation.
As many others already mentioned throw with try...catch is not really an approach which can be called "solution". It is more a hack with many unwanted side effects.
The only way to do this WITHOUT ANY MUTATIONS is by using a second compare function, which decides whether to continue or stop. To still avoid a for-loop, it has to be solved with a recursion.
The code:
function reduceCompare(arr, cb, cmp, init) {
return (function _(acc, i) {
return i < arr.length && cmp(acc, arr[i], i, arr) === true ? _(cb(acc, arr[i], i, arr), i + 1) : acc;
})(typeof init !== 'undefined' ? init : arr[0], 0);
}
This can be used like:
var arr = ['a', 'b', 'c', 'd'];
function join(acc, curr) {
return acc + curr;
}
console.log(
reduceCompare(
arr,
join,
function(acc) { return acc.length < 1; },
''
)
); // logs 'a'
console.log(
reduceCompare(
arr,
join,
function(acc, curr) { return curr !== 'c'; },
''
)
); // logs 'ab'
console.log(
reduceCompare(
arr,
join,
function(acc, curr, i) { return i < 3; },
''
)
); // logs 'abc'
I made an npm library out of this, also containing a TypeScript and ES6 version. Feel free to use it:
https://www.npmjs.com/package/array-reduce-compare
or on GitHub:
https://github.com/StefanJelner/array-reduce-compare
You could to write your own reduce method. Invoking it like this, so it follows same logic and you control your own escape / break solution. It retains functional style and allows breaking.
const reduce = (arr, fn, accum) => {
const len = arr.length;
let result = null;
for(let i = 0; i < len; i=i+1) {
result = fn(accum, arr[i], i)
if (accum.break === true) {
break;
}
}
return result
}
const arr = ['a', 'b', 'c', 'shouldnotgethere']
const myResult = reduce(arr, (accum, cur, ind) => {
accum.result = accum.result + cur;
if(ind === 2) {
accum.break = true
}
return accum
}, {result:'', break: false}).result
console.log({myResult})
Or create your own reduce recursion method:
const rcReduce = (arr, accum = '', ind = 0) => {
const cur = arr.shift();
accum += cur;
const isBreak = ind > 1
return arr.length && !isBreak ? rcReduce(arr, accum, ind + 1) : accum
}
const myResult = rcReduce(['a', 'b', 'c', 'shouldngethere'])
console.log({myResult})
Another simple implementation that I came with solving the same issue:
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
}
If you want to chain promises sequentially with reduce using the pattern below:
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());
But need to break according to something happening inside or outside a promise
things become a little bit more complicated because the reduce loop is terminated before the first promise is executed, making truncating the array in the promise callbacks useless, I ended up with this implementation:
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;
}
})
}
Then you can do something like this:
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);
});
}
I solved it like follows, for example in the some method where short circuiting can save a lot:
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)
UPDATE
Away more generic answer could be something like the following
const escReduce = (arr, fn, init, exitFn) => {
try {
return arr.reduce((...args) => {
if (exitFn && exitFn(...args)) {
throw args[0]
}
return fn(...args)
}, init)
} catch(e){ return e }
}
escReduce(
Array.from({length: 100}, (_, i) => i+1),
(acc, e, i) => acc * e,
1,
acc => acc > 1E9
); // 6227020800
give we pass an optional exitFn which decides to break or not

Categories