Apply callback to 2 arbitrary length arrays using functional programming in JavaScript - javascript

Given that these function declarations exist within scope:
function compose() {
var funcs = arguments;
return function() {
var args, i;
args = arguments;
for (i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(null, args)];
}
return args[0];
};
}
function id(x) {
return x;
}
function partial(targetfn) {
var arity = targetfn.length;
return function fn() {
if (arguments.length < arity) {
return Function.prototype.bind.apply(fn, [null].concat(
Array.prototype.slice.call(arguments)
));
} else {
return targetfn.apply(null, arguments);
}
}
}
Having this callback function as an example:
function cb(x, y) {
console.log(x);
console.log(y);
}
Is it possible using FP techniques (such as argument binding, composition and partial application) to apply the callback function to 2 arrays of arbitrary length? E.g.: ["foo", "bar", "baz", "quux"] and [12, 42]? The expected output in this case would be:
foo
12
foo
42
bar
12
bar
42
baz
12
...and so on
Since Array.prototype.forEach and Array.prototype.map pass 3 arguments to the callback function, it's at least possible to iterate over a single array using function composition as follows:
["foo", "bar", "baz", "quux"].forEach(compose(cb, id))
It's also possible to map an array into partially applied functions like so:
[12, 42].map(compose(partial(cb), id))
I've been looking into using Function.prototype.apply, Function.prototype.bind and Function.prototype.call, combining them with Array.prototype.forEach and Array.prototype.map, so far without success. I'm trying hard not to introduce more function declarations / expressions.
Update
Thanks to Mulan, the problem can be solved by splitting it into the part that computes the cartesian product of lists and applying the resulting list to a given function.
function product() {
var args = [].slice.call(arguments);
return args.reduce(function(prev, cur) {
var ret = [];
prev.forEach(function(x) {
cur.forEach(function(y) {
ret.push(x.concat(y));
});
});
return ret;
}, args.shift().map(function(val) {
return [val];
}));
}
product(
["foo", "bar", "baz", "quux"],
[12, 42]
).forEach(Function.prototype.apply.bind(cb, null));

I think you might be making more work for yourself by forcing compose and partial where they're not particularly useful. It seems like you're looking to compute a product of input arrays. The result is an array of products, each of which contains a unique grouping of elements taken from the input arrays -
function product(a, ...more) {
if (more.length == 0)
return a.map(v => [v])
else
return a.flatMap(v => product(...more).map(p => [v, ...p]))
}
function cb(x,y) {
console.log(x, y)
}
product(["foo", "bar", "baz", "quux"], [12, 42])
.forEach(p => cb(...p))
.as-console-wrapper { min-height: 100%; top: 0; }
If you really want to use partial, you can accept a callback cb as the first argument, and then all arrays to multiply thereafter. Each step in product applies one argument to cb. The result is an array of products where each product is an array of partially applied functions, which can be executed by calling cb() -
function partial(f, ...x) {
return (...y) => f(...x, ...y)
}
function product(cb, a, ...more) {
if (more.length == 0)
return a.map(v => partial(cb, v))
else
return a.flatMap(v => product(partial(cb, v), ...more))
}
const cb = partial(console.log, "i would like")
const res = product(cb,
["๐Ÿ”", "๐ŸŒฎ", "๐Ÿฅช"],
["๐ŸŸ", "๐Ÿฅจ"],
["๐Ÿฅค", "โ˜•๏ธ"]
)
res.forEach(cb => cb())
.as-console-wrapper { min-height: 100%; top: 0; }

Related

Chain methods and call function one by one

so basically I need to implement a lazy evaluation. .add() takes a function as parameter and any other arbitrary arguments. The function that is passed as an argument is run later (when evaluate is called) with other arguments(if any) as parameters to that function.
Basically my issue stands when i run .evaluate() which takes an array as parameter, the functions that were passed to add() as parameters are not called one at a time and returning a result and have it as a parameter for the next.
class Lazy {
constructor() {
this.functions = [];
this.counter = 0;
this.resultedArray = [];
}
add(func, ...args) {
if (args.length > 0) {
this.counter++;
const argsArrayNumeric = args.filter((item) => typeof item === "number");
this.functions.push({
func: func,
args: args,
});
} else {
if (func.length <= args.length + 1 || func.length === args.length) {
this.counter++;
this.functions.push({ func: func });
} else {
console.log("Missing parameters for " + func.name + " function");
}
}
return this;
}
evaluate(array) {
if (this.counter > 0) {
let result;
this.functions.map((obj, index) => {
array.map((item, index) => {
console.log(obj);
if (obj.func && obj.args) {
result = obj.func(item, ...obj.args);
} else {
result = obj.func(item);
}
this.resultedArray.push(result);
});
});
console.log(this.resultedArray);
} else {
console.log(array);
}
}
}
const s = new Lazy();
const timesTwo = (a) => {
return a * 2;
};
const plus = (a, b) => {
return a + b;
};
s.add(timesTwo).add(plus, 1).evaluate([1, 2, 3]);
//correct result is [3,5,7] but i have [ 2, 4, 6, 2, 3, 4 ]
There are a few problems in evaluate:
push will be executed too many times when you have more than one function.
Don't use map when you don't really map. .map() returns a result.
From the expected output (in the original version of the question) it seems you need to apply the functions from right to left, not from left to right.
this.counter does not really play a role. The length of the this.functions array should be all you need.
the result should not be printed in the method, but returned. It is the caller's responsibility to print it or do something else with it.
All this can be dealt with using reduce or reduceRight (depending on the expected order) like this:
evaluate(array) {
return this.functions.reduceRight((result, obj) =>
result.map((item) => obj.func(item, ...(obj.args || [])))
, array);
}
And in the main program, print the return value:
console.log(s.add(plus, 1).add(timesTwo).evaluate([1, 2, 3]));
The add method has also some strange logic, like:
When the first if condition is false, then the else block kicks in with args.length == 0. It is then strange to see conditions on args.length... it really is 0!
If the first condition in func.length <= args.length + 1 || func.length === args.length is false, then surely the second will always be false also. It should not need to be there.
argsArrayNumeric is never used.
All in all, it seems the code could be reduced to this snippet:
class Lazy {
constructor() {
this.functions = [];
}
add(func, ...args) {
if (func.length > args.length + 1) {
throw ValueError(`Missing parameters for ${func.name} function`);
}
this.functions.push({ func, args });
return this;
}
evaluate(array) {
return this.functions.reduceRight((result, obj) =>
result.map((item) => obj.func(item, ...(obj.args || [])))
, array);
}
}
const timesTwo = (a) => a * 2;
const plus = (a, b) => a + b;
const s = new Lazy();
console.log(s.add(plus, 1).add(timesTwo).evaluate([1, 2, 3])); // [3, 5, 7]
I think you're working too hard at this. I believe this does almost the same thing (except it doesn't report arity errors; that's easy enough to include but doesn't add anything to the discussion):
class Lazy {
constructor () {
this .fns = []
}
add (fn, ...args) {
this .fns .push ({fn, args});
return this
}
evaluate (xs) {
return xs .map (
x => this .fns .reduce ((a, {fn, args}) => fn (...args, a), x)
)
}
}
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const s = new Lazy () .add (timesTwo) .add (plus, 1)
console .log (s .evaluate ([1, 2, 3]))
But I don't actually see much of a reason for the class here. A plain function will do much the same:
const lazy = (fns = [], laze = {
add: (fn, ...args) => lazy (fns .concat ({fn, args})),
evaluate: (xs) => xs .map (
x => fns .reduce ((a, {fn, args}) => fn (...args, a), x)
)
}) => laze
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const s = lazy () .add (timesTwo) .add (plus, 1)
console .log (s .evaluate ([1, 2, 3]))
This is slightly different in that s is not mutated on each add, but a new object is returned. While we could change that and mutate and return the original object easily enough, the functional programming purist in me would actually consider moving more in that direction. In fact, all we're maintaining here is a list of {fn, args} objects. It might make the most sense to take that on directly, like this:
const add = (lazy, fn, ...args) => lazy .concat ({fn, args})
const evaluate = (lazy, xs) => xs .map (
x => lazy .reduce ((a, {fn, args}) => fn (...args, a), x)
)
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const q = []
const r = add (q, timesTwo)
const s = add (r, plus, 1)
// or just
// const s = add (add ([], timesTwo), plus, 1)
console .log (evaluate (s, [1, 2, 3]))
In this version, we don't need a Lazy constructor or a lazy factory function, as our data structure is a plain array. While this is the format I prefer, any of these should do something similar to what you're trying.
Update
Based on comments, I include one more step on my journey, between the first and second snippets above:
const lazy = () => {
const fns = []
const laze = {
add: (fn, ...args) => fns .push ({fn, args}) && laze,
evaluate: (xs) => xs .map (
x => fns .reduce((a, {fn, args}) => fn (...args, a), x)
)
}
return laze
}
const timesTwo = (a) => a * 2
const plus = (a, b) => a + b
const s = lazy () .add (timesTwo) .add (plus, 1)
console .log (s .evaluate ([1, 2, 3]))
This version may be more familiar than the second snippet. It stores fns in a closure, hiding implementation details in a way we often don't with JS classes. But that list is still mutable through the add method of the object we return. Further ones proceed down the road to immutability.

Implement function and return new list js

I've only been studying javascript for three weeks now and need help on a task.
I want to implement a function, called test, which takes a list and a function as arguments. The test function should call the function that was submitted as an argument once for each value in the list, with the list value in question as an argument, and will return a NEW list containing only the list values โ€‹โ€‹for which the argument function returned true. And I want to do that WITHOUT using the filter() method.
How can I think here? Is my code below a good start? I appreciate all the help I can get here so I can understand this.
let x = ["1", "2", "3"];
function test(x, s) {
for (let i = 0; i < x.length; i++) {
}
return
}
The code you provided is a good start, it provides a way of looping through all the elements in the list, which is a good starting point. I do suggest however that you change the names of your arguments so that they better represent the data they hold.
Your next step is to call the function passed in as an argument (f) for each element in your list and check whether or not it returns true. If it does, then you can add this element to a new list (which holds the list of values to return).
Once your loop is complete, you can return this new list. Take a look at an example below to get an understand of how you might implement this:
let x = [1, 2, 3, 4];
function test(lst, f) {
let new_list = [];
for (let i = 0; i < lst.length; i++) {
if(f(lst[i])) { // check if the function gives back a "truthy" value
new_list.push(lst[i]);
}
}
return new_list;
}
let result = test(x, function(list_element) {
return list_element % 2 === 0; // return true if element is even
});
console.log(result);
You could also achieve this in a number of different ways, such as using .reduce() and a ternary to make the method nice and concise:
const x = [1, 2, 3, 4];
const test = (lst, f) =>
lst.reduce((acc, elem) => f(elem) ? [...acc, elem] : acc, []);
const res = test(x, n => n % 2 === 0);
console.log(res);
You can create an empty array at the start of your function, that you populate while reading your array before returning it.
As you don't want to use filter, I will provide a solution using forEach, and an other using a basic for loop.
let x = ["1", "2", "3"];
function testFor(list, testFct) {
const resultArray = [];
for (let i = 0; i < list.length; i++) {
if (testFct(list[i])) {
resultArray.push(list[i]);
}
}
return resultArray;
}
function testForeach(list, testFct) {
const resultArray = [];
list.forEach((element) => {
if (testFct(element)) {
resultArray.push(element);
}
})
return resultArray;
}
console.log(testFor(x, (el) => el % 2));
console.log(testForeach(x, (el) => el % 2));
But at the end of the day, I don't see why you could not use filter, as it returns a new array anyway.
If you want to do that in a simple way is this -->
let x = ["1", "2", "3"];
function myFunction(element){
if (element == "1" || element == "2"){
return true
}
return false
}
function test(list, myFunction) {
let newList = [];
for (let i = 0; i < list.length; i++) {
let element = list[i];
if (myFunction(element)){
newList.push(element);
}
}
return newList
}
test(x, myFunction)
There is another simpler way using filter -->
let x = ["1", "2", "3"];
function myFunction(element){
if (element == "1" || element == "2"){
return true
}
return false
}
function test(list, myFunction) {
return list.filter(myFunction)
}
test(x, myFunction)

Implementing lazy evaluation for `reduceRight()` function in JavaScript

A problem in the codewars.com:
https://www.codewars.com/kata/foldr/train/javascript
Define a foldr function for array which implement the same function like built-in function reduceRight(). The problem is very abstract because I knew few about functional programming.
I find the most important is how to implement lazy-evaluation in the JavaScript. But I don't know how to deal with it.
For example, we have two functions, indexOf and logging, indexOf(x) is a function which will be called as arguments in the foldr method, logging is a wrapper function which tell us how many times indexOf(x) could be called.
const indexOf = y => function (cur, acc) {
if (cur === y) {
return 0
} else {
return acc + 1 || -1
}
};
const logging = fn => function logging(...a) {
i++;
return fn(...a);
};
If we implement it without laziness and use recursion:
Object.defineProperty(Array.prototype, "foldr", {
value: function foldr(fn, z) {
return function _foldr(a) {
if (a.length === 0) {
return z
}
return fn(a[0], _foldr(a.slice(1)))
}(this);
}
});
let i = 0
let x = [1, 2, 3].foldr(logging(indexOf(1)), -1)
console.log(`x: ${x}`) // x: 0
console.log(`i: ${i}`) // i: 3
The variable i shows that the function has been called 3 times, the whole array has been iterated. However, if we observe the indexOf funciton we'll find that we don't need to iterate the whole array if we use lazy evaluation.
In the first level of recursion
indexOf(1)(a[0], _foldr(a.slice(1))) equals indexOf(1)(1, _foldr([2,3])), because cur === y, it should return 0 immediately and doesn't need to evaluate the second argument _foldr([2,3]). So in the test case of the codewars.com, the i should be 1.
How could I deal with it?
You are close ...:
fn(a[0], () => _foldr(a.slice(1)))
If you pass a function instead of the accumulator value, the function can decide to evaluate the accumulator or not:
const indexOf = y => function (cur, acc) {
if (cur === y) {
return 0; // acc() was not called, ends here
} else {
return acc() + 1 || -1; // acc() gets called, traversal goes on
}
};
I find the solution. Use Object.prototype.valueOf()
The valueOf() method returns the primitive value of the specified object.
Because we'd like to call the function when we do some algorithms such as return acc + 1 || -1.
We could use:
Object.defineProperty(Array.prototype, 'foldr', {
value(fn, z) {
let _foldr = (a) => {
if (!a.length) return z;
let r = fn(a[0], { valueOf: () => _foldr(a.slice(1)) });
return (r.valueOf) ? r.valueOf() : r;
};
return _foldr(this);
},
});

piping functions in JavaScript

How can I have a JavaScript function let's say piper() which takes several functions as its arguments and it returns a new function that will pass its argument to the first function, then pass the result to the second, then
pass the result of the second to the third, and so on, finally returning the output of the last function.
Something like piper(foo, fee, faa)(10, 20, 30) would be equivalent to calling faa(fee(foo(10,20,30))).
ps:
It was a part of an interview, that I did few days ago.
For an arbritrary number of functions you could use this ES6 function:
function piper(...fs) {
return (...args) => fs.reduce((args,f) => [f.apply(this,args)],args)[0];
}
// Example call:
var result = piper(Math.min, Math.abs, Math.sqrt)(16, -9, 0)
// Output result:
console.log(result);
The same in ES5 syntax:
function piper(/* functions */) {
var fs = [].slice.apply(arguments);
return function (/* arguments */) {
return fs.reduce(function (args,f) {
return [f.apply(this,args)];
}.bind(this), [].slice.apply(arguments))[0];
}.bind(this);
}
// Example call:
var result = piper(Math.min, Math.abs, Math.sqrt)(16, -9, 0)
// Output result:
console.log(result);
Enjoy. Pure ES5 solution. Preserves this.
function piper(){
var i = arguments.length,
piped = arguments[ --i ];
while( --i >= 0 ){
piped = pipeTwo( arguments[ i ], piped );
}
return piped;
}
function pipeTwo( a, b ){
return function(){
return a.call( this, b.apply( this, arguments ) );
}
}
Or, if you want the fancy solution.
function piperES6( ...args ){
return args.reverse().reduce( pipeTwo );
}
Loops can be reversed depending on the desired direction.
Very similar to #trincot's answer (preserves context), but composes in the correct order and is marginally faster since it does not create intermediary arrays:
const piper = (...steps) => function(...arguments) {
let value = steps[0].apply(this, arguments);
for (let i = 1; i < steps.length; ++i) {
value = steps[i].call(this, value);
}
return value;
};
// Usage:
let p = piper(
x => x + 1,
x => x * 2,
x => x - 1
);
console.log(p(2)); // 5
Here is an alternative answer involving method chaining. I shall use ES6, though of course this can be transpiled to ES5. On benefit of this solution is that is has a very succinct TypeScript counterpart with perfect typeability.
class Pipe {
constructor(value) {
this.value = value;
}
then(f) {
return new Pipe(f(this.value));
}
}
const pipe = value => new Pipe(value);
// Example
const double = x => 2 * x;
pipe(42).then(double).then(console.log); // 84
const result = pipe(42).then(double).then(double).value;
console.log(result); // 168
A simple solution based on JS higher-order functions usage:
function pipe(...rest) {
return x => rest.reduce((y, f) => f(y), x);
}
Usage:
pipe((a) => a + 1, (a) => a * 2)(3) // 8
pipe((a) => a + 1, (a) => a * 2)(2) // 2
function f(f1, f2, f3){
return (args => f3(f2(f1(args))));
}
I think what you are trying to do is chaining.
var funct={
total:0,
add:function(a) {
console.log(funct.total,funct.total+a);
funct.total+=a;
return funct;
}
};
funct.add(5).add(6).add(9);

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