onceCopy function (testFunc) {
const copyFunc = (a) => {
const copyFunc2 = (b) => {
return testFunc(a);
};
return copyFunc2;
};
return copyFunc;
};
So the function returns the inner function upon first invocation.
Then returns the inner function of the inner function of the second invocation.
Then the second inner function (third invocation) actually returns the passed argument in the parent function and only invokes it with the character we gave it on the second invocation.
Ideally I want to achieve what I'm achieving over many invocations after only the first one if that makes sense.
Edit: Yes sorry, _.once.
Edit: so first invocation onceCopy SHOULD hold a copy of the Func passed
Second invocation SHOULD trigger the copy and gives an ouput
Third invocation SHOULD give the result of the second invocation so should the fourth, fifth, sixth and so on...
My function does do this, but on the second invocation it stores a function (copyFunc2) again, but I just made that because I need somewhere to store "a".
so like we have
function multiplyBy3 (a) {return a*3}
and then once copy stores a copy of multiplyBy3
const actualFunction = onceCopy(multiplyBy3)
then upon second and third invocation what I want
actualFunction(1) = 3
actualFunction(66) = 3
so the passed function ONLY RUNS ONCE
Cant explain more than this, its in the lodash docs.
I'm not familiar with the function you're trying to reimplement, so feel free to correct me if I misunderstood. To wrap a function and ensure it's only called once you don't need multiple nested wrappings, only one with some state.
You need to keep track of whether you already have a result to return (hasResult) and if so, what that result is (result). Keeping these two variables separate allows you to cover the case when result is undefined while keeping the code easy to read and understand.
function once(wrappedFunction) {
let hasResult = false;
let result;
return (...args) => {
if (hasResult) {
return result;
}
result = wrappedFunction.apply(this, args);
hasResult = true;
return result;
}
}
// An example function to wrap
function multiply(a, b) {
return a * b;
}
const demoFunction = once(multiply)
console.log(demoFunction(1, 2)); // 2
console.log(demoFunction(3, 4)); // 2
console.log(demoFunction(5, 6)); // 2
Is this what you were looking for?
The initial function return is kept on subsequent calls. I used a second variable called in case the first call returns undefined, which should also be returned on subsequent calls.
const once = (onceFn) => {
let called;
let value;
return (...args) => {
if (called) return value;
called = true;
return (value = onceFn(...args));
};
};
function multiplyBy3(a) {
return a * 3;
}
const fn = once(multiplyBy3);
console.log(fn(3)); // 9
console.log(fn(66)); // 9
After calling the function for the 1st time, and getting the result, create a new function that returns the result, and use it whenever the wrapped function is called:
const once = fn => {
let func
return (...args) => {
if(func) return func()
const result = fn(...args)
func = () => result
return result
}
}
const multiply = (a, b) => a * b
const demoFunction = once(multiply)
console.log(demoFunction(1, 2)); // 2
console.log(demoFunction(3, 4)); // 2
console.log(demoFunction(5, 6)); // 2
The Lodash flow() function works, as shown in this question, by giving it a bunch of functions in an array. The result of the previous function will be applied to the next one.
And this is exactly what is problematic for my use case, as it throws away any arguments provided. I need to pass them on to the next function in line, instead of just the return value *).
*) Yes, the return value should get returned modified and be passed on the next function
Using flow() is just what I came up with. Any other solution achieving the goal is ok as well.
What it does:
_.flow( [ firstFn, secondFn ] )
( first, second, third ) => firstFn( first, second, third ) => return first * first
( first ) => secondFn( first )
What it should do:
_.flow( [ firstFn, secondFn ] )
( first, second, third ) => firstFn( first, second, third ) => return first * first
( first, second, third ) => secondFn( resultFromFirstFn, second, third )
Here's some code to demo it:
const checkLog = ( val, attr, group ) => {
console.log( val, attr, group )
return val
}
// This basically is "easy to use"-API and what will be mostly used.
const Callbacks = {
demo: [ _.trim, _.toLower, checkLog ],
}
// _.cond() is just a switch
// [ <condition-to-exec-cb>, <cb-if-cond-true> ]
const cond = _.cond( _.map(
[
[ _.stubTrue, Callbacks.demo ],
],
// Monkey patching _.flow() to each array of cond/cb above
cb => _.set( cb, '1', _.flow( _.last( cb ) ) )
) )
const res = _.map( {
" city": " Sample City",
"street": "Sample Street",
}, cond )
console.log( res )
Note: In case you wonder about why I write it like this: There's a single line comment that points to which part will get altered and extended a lot in the future and this must, due to the target group working there, be simple. So it's just an array of stacked callbacks.
The problem with _.flow() is that it expects everything after the first argument to be a unary function so it can pass the result through all of them. This presents a challenge when you want to apply the same set of arguments to all function and only alter the first one. There are a few ways you could go about this.
Using Lodash
You could leverage _.partialRight to do a partial allocation of every function. partialRight will apply arguments from right to left depending on how many arguments the function takes.
const fn = (a, b, c, d) => console.log(a, b, c, d);
// behaves similar to (a, b) => console.log(a, b, "yak", "zebra")
const newFn = _.partialRight(fn, "yak", "zebra");
newFn("alpaca", "beaver");
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
However you can still pass arguments left to right and this would push the partially applied arguments to right:
const fn = (a, b, c, d) => console.log(a, b, c, d);
// behaves similar to (a, b) => console.log(a, b, "yak", "zebra")
const newFn = _.partialRight(fn, "yak", "zebra");
newFn("alpaca", "beaver", "cat");
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
Assuming all of the function will have at most take n amount of arguments we can:
Get an array of n arguments to be applied to all functions.
Get all functions.
Do partialRight on all functions using all but the first argument. Since we've applied n-1 arguments to each, now all of them can be called as if they are unary.
Use flow on the new functions from step 3.
Start the chain using the first argument.
This will then call all functions such that the first argument is the last result and the second and onwards arguments would be based on the initial ones thus always the same for all functions
function nAryFlow(argsArray, ...fns) { //1. and 2. - get arguments and functions
const start = argsArray[0];
const rest = argsArray.slice(1);
const convertedFns = fns.map(f => _.partialRight(f, ...rest)) //3. Turn all functions into unary ones
return _.flow( //4. `flow` through the functions
...convertedFns
)(start); //5. use the initial fist argument to kick things off
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const args = ["Alice", "Bob", "Carol"];
const result = nAryFlow(args, fn1, fn2, fn3);
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
Without using Lodash
All of the above can very easily be done without Lodash. Instead, we can use Array#reduce to go through all of the functions and apply the arguments. This time, we're directly applying them instead of pre-processing the functions but the overall operation and effect is the same:
The first function takes all the arguments.
Any further function takes the result of the last function and the second argument onwards from the start:
function nAryFlow(argsArray, ...fns) {
const start = argsArray[0];
const rest = argsArray.slice(1);
return fns.reduce((last, f) => f(last, ...rest), start);
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const args = ["Alice", "Bob", "Carol"];
const result = nAryFlow(args, fn1, fn2, fn3);
console.log(result)
Variation using more higher order functions
Just as a variation, this could be split up into multiple higher order functions which might produce a nicer syntax for some situations nAryFlow(f1, f2, f3, fN)(arg1, arg2, arg3, argN):
function nAryFlow(...fns) {
return function (...args) {
const start = args[0];
const rest = args.slice(1);
return fns.reduce((last, f) => f(last, ...rest), start);
}
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const chain = nAryFlow(fn1, fn2, fn3);
const result1 = chain("Alice", "Bob", "Carol");
const result2 = chain("Rachel Green", "Monica Geller", "Chandler Bing");
console.log(result1);
console.log(result2);
Using a Functor
For a slightly different take here, you can use an algebraic structure called Functor to ease the syntax. The essential thing about functors is that they have a .map() method that accepts a function. If you're reminded of Array#map then you're not wrong.
The basic idea is that the functor holds a value and allows it to be modified with functions via .map(). It can then dictate how the function is applied to its value. The result of .map is always the same type of functor as what was already mapped, so you can always continue mapping and be sure that the application would be the same every time. With arrays you always get a new array of the same length with every member transformed. Other functors can apply the function given to .map() different to what an array does but it would always be consistent.
So, background done, here is how this functor can look like:
class NAryFlow {
constructor(argsArray) {
this.value = argsArray[0];
this.rest = argsArray.slice(1);
}
static of(argsArray) {
return new NAryFlow(argsArray);
}
map(fn) {
return NAryFlow.of(
[ fn(this.value, ...this.rest), ...this.rest ]
);
}
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const result = NAryFlow.of(["Alice", "Bob", "Carol"])
.map(fn1)
.map(fn2)
.map(fn3)
.value;
console.log(result)
A similar idea as the two others above - we take the arguments, and apply them all to each function we give to .map(). Every next time we call .map() the first argument would be the last result.
And here is a slight variation using ES6 getters. I think it has a slightly better syntax but wanted to keep the previous implementation simpler.
class NAryFlow {
constructor(argsArray) {
this.args = argsArray;
}
static of(argsArray) {
return new NAryFlow(argsArray);
}
map(fn) {
return NAryFlow.of(
[ fn(...this.args), ...this.rest ]
);
}
get value() {
return this.args[0];
}
get rest() {
return this.args.slice(1);
}
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const result = NAryFlow.of(["Alice", "Bob", "Carol"])
.map(fn1)
.map(fn2)
.map(fn3)
.value;
console.log(result)
flow doesn't throw any arguments, it's just how javascript functions work, they don't respect the a particular signature, you can call a function that only takes 1 parameter with 100 parameters and that won't throw an error.
Also flow is defined such that it passes the initial parameters all to the first function regardless of whether it's going to use them all or not.
You can create your own flow function(since it is a simple one) that only passes the exact amount of parameters a function needs and keep the rest for the rest of functions. To know how many parameters a function takes, you can check its length property like so:
function flow(funcs) {
const length = funcs.length;
for(let func of funcs) {
if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
}
return function(...args) {
let result = args[0];
for(let func of funcs) {
result = func.apply(this, args.slice(0, func.length)); // call this function with only as many args as it needs
args = [result, ...args.slice(func.length)]; // args becomes the result + the rest of args after the previous call
}
return result;
}
}
Note: This won't work if one of the functions uses the rest parameters, in that case length will return 0. You can't know how many parameters that functions takes. If you wan to call that function with all the available parameters, then just change the for loop above to:
for(let func of funcs) {
result = func.apply(this, args.slice(0, func.length || +Infinity)); // if 'func.length' is 0, '+Infinity' is used instead which will use all the available args
args = [result, ...args.slice(func.length || +Infinity)]; // same goes here, '.slice(+Infinity)' will result in an empty array. 'result' will always be present which is the expected behavior
}
Demo:
function flow(funcs) {
const length = funcs.length;
for(let func of funcs) {
if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
}
return function(...args) {
let result = args[0];
for(let func of funcs) {
result = func.apply(this, args.slice(0, func.length)); // call this function with only as many args as it needs
args = [result, ...args.slice(func.length)]; // args becomes the result + the rest of args after the previous call
}
return result;
}
}
function square(n) {
return n * n;
}
function add(a, b, c) {
return a + b + c;
}
console.log(flow([square, add])(2, 3, 4));
Is there any way that I can write the function so that when I call it even by passing arguments differently, it still outputs the same value
I am new to JavaScript and was recently asked this question in an interview -
Write a function sum which when called like sum(2)(3) or sum(2, 3) should return 5.
This could be done individually as follows
function sum(a){
return function(b){
return a+b
}
}
or
function sum(a, b){
return a+b
}
If I follow the first code, it won't execute sum(a, b) and of course the second code will not support sum(a)(b). Is there any way that I can merge the two code snippets so that it executes doesn't matter how I call it ?
You'll need to check how many arguments were passed. If two are passed, return the added values; otherwise, return a function that, when called, returns its argument plus the closure's argument:
const sum = (...args) => {
if (args.length === 2) {
return args[0] + args[1];
}
return arg => arg + args[0];
};
console.log(sum(2)(3));
console.log(sum(2, 3));
More generally, you can create a makeSum function to handle when the total number of arguments to accept is an arbitrary number:
const makeSum = totalArgCount => {
const fn = (...args) => {
if (args.length === totalArgCount) {
return args.reduce((a, b) => a + b, 0);
}
return fn.bind(undefined, ...args);
};
return fn;
};
const sum2 = makeSum(2);
console.log(sum2(2)(3));
console.log(sum2(2, 3));
const sum4 = makeSum(4);
console.log(sum4(2)(3)(4)(5));
console.log(sum4(2, 3, 4)(5));
function sum(a, b) {
return b !== undefined ? (a + b) : function(b) { return a + b; }
}
console.log(sum(1,2));
console.log(sum(1)(3));
You can do it like this, by checking the b argument. If it is undefined, then you return a function. If it is not, you return the sum of the numbers.
Of course you could also check the both arguments are numbers, but this is simple version to illustrate what you want.
This question already has answers here:
When should I use a return statement in ES6 arrow functions
(6 answers)
Closed 4 years ago.
Why is this-
const squareList = (arr) => {
"use strict";
const sqr = arr.filter((numbers) => {
numbers > 0 && Number.isInteger(numbers)
}).map((numbers) => {
Math.pow(numbers, 2)
})
return sqr;
};
functionally different to this-
const squareddList = (arr) => {
"use strict";
const sqr = arr.filter((numbers) => numbers > 0 && Number.isInteger(numbers)).map((numbers) => Math.pow(numbers, 2))
return sqr;
}
when the only difference between them is whitespace and curly braces?
When you pass an arbitrary array to both functions, the first returns an empty array and the second returns an array which has been filtered to only include positive integers and then squared.
Arrow functions without curly braces implicitly return the result of the expression in the function body so:
const someFunction = () => 10
someFunction(); // Returns 10
is equivalent to
const someFunction = () => {
return 10;
}
someFunction(); // Returns 10
and is NOT equivalent to:
const someFunction = () => {
10;
}
someFunction(); // Returns undefined
The difference is because in the first one you don't return the values.
In arrow function, if you put curly brackets you need to return the value, if not the values are returned.
const squareList = (arr) => {
"use strict";
const sqr = arr.filter((numbers) => {
return numbers > 0 && Number.isInteger(numbers)
}).map((numbers) => {
return Math.pow(numbers, 2)
})
return sqr;
};
This will work.
This question already has answers here:
ES6 immediately invoked arrow function
(4 answers)
Closed 4 years ago.
Babel is choking on code that uses the module pattern with an arrow function.
If I try to process a file which reads:
const lib = (() => {
function sum(a, b) { return a + b; }
function mult(a, b) { return a * b; }
return {
sum,
mult
};
}());
Babel gives a SyntaxError
{ SyntaxError: /home/david/Sync/ebs/office/fake.js: Unexpected token, expected "," (9:1)
7 | mult
8 | };
> 9 | }());
| ^
10 |
at Parser.raise (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:3939:15)
at Parser.unexpected (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5248:16)
at Parser.expect (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5236:28)
at Parser.parseParenAndDistinguishExpression (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:6454:14)
at Parser.parseExprAtom (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:6284:21)
at Parser.parseExprSubscripts (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5924:21)
at Parser.parseMaybeUnary (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5903:21)
at Parser.parseExprOps (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5812:21)
at Parser.parseMaybeConditional (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5784:21)
at Parser.parseMaybeAssign (/home/david/Sync/ebs/office/node_modules/#babel/parser/lib/index.js:5731:21)
pos: 137,
loc: Position { line: 9, column: 1 },
code: 'BABEL_PARSE_ERROR' }
However, if I change the code to use an older style function, like
const lib = (function () {
function sum(a, b) { return a + b; }
function mult(a, b) { return a * b; }
return {
sum,
mult
};
}());
Babel seems to process the file with no issues.
What am I doing wrong? Is there a genuine syntax problem with the arrow function code I am not aware of?
Try moving your paren inside, like this:
})();
const lib = (() => {
function sum(a, b) { return a + b; }
function mult(a, b) { return a * b; }
return {
sum,
mult
};
})();
console.log(lib)
In ES6, an arrow function basically consists of the following three components:
() => {}
So in order to invoke it immediately it should be wrapped in parentheses and then called, like this:
const lib = (() => {
//do something
})();
Due to the nature of arrow functions and how they are interpreted, (E.g. :
const x = () => ({ objectPropValues });
const x = () => { functionBody };
const x = () => conciseFunctionBody;
I imagine that the second arrow function in
const x = (() => {functionBody})();
const x = (() => {functionBody}());
Can not be allowed, and isn't.
This can probably be confirmed by looking carefully at the ECMA 6 specification for arrow functions.