Callbacking a function with arguments - javascript

I have an exercise where I have to give three arguments: function and two numbers.
The function which I give has to be activated (each) after x miliseconds for y miliseconds.
I wanted to make helper like this:
function helper(string) {
console.log("Printing string which was given: " + string)
}
but when I do it like this and I try to enable my function ex1_4(helper("some string"), 500,5000) I get an error that Callback must be a function
What am I doing wrong?
function ex1_4(func, x,y) {
const resFunction = setInterval((func), x)
const stop = setTimeout(() => {clearInterval(resFunction)}, y)
}
function helper(string) {
console.log("Printing string which was given: " + string)
}
ex1_4(helper("some string"),500,5000)

helper("some string")
Is a function call which returns a value, in your case it is undefined. If you want to make it into a callback, you need to wrap it in a function like so:
() => helper(“some string”)
In your code:
ex1_4(() => helper("some string"),500,5000)

When you add helper("some string"), you're actually executing the method, instead of sending the method to ex1_4. You should type something like ...
ex1_4(helper,500,5000) // no parenthesis
... just like you did with setInterval((func), x).
However, you want to add parameters to helper, and in this case, you can use bind. You should really learn about bind(), call(), and apply().
ex1_4(helper.bind(null, "some string"),500,5000)
function ex1_4(func, x,y) {
const resFunction = setInterval((func), x)
const stop = setTimeout(() => {clearInterval(resFunction)}, y)
}
function helper(string) {
console.log("Printing string which was given: " + string)
}
ex1_4(helper.bind(null, "some string"),500,5000)

Related

Converting a string to a function in JavaScript

I am playing around with some office JavaScript and attempting to create an executable function from a string that is received from an API call.
The office JavaScript task pane for Excel makes a call to an external API on button click, and returns a function in the form of a String object. To make this into a function object, I have used:
var executable = new Function(response)
executable();
Unfortunately, nothing is happening, it doesn't seem to be calling the function at all.
After some debugging, I believe the reason it isn't getting called is because the response string object is already a full function, and new Function() is wrapping the response in another layer of function.
Response is:
async function highlightCells() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sheet1");
const range = sheet.getRange();
range.format.fill.color = "yellow";
await context.sync();
console.log("Called");
});
}
And executable is resolving to:
function anonymous() {
async function highlightCells() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getItem("Sheet1");
const range = sheet.getRange();
range.format.fill.color = "yellow";
await context.sync();
console.log("Called");
});
}
}
Any ideas how to prevent the additional function wrapper from appearing? As you can see the response object is already a full function.
Do I need to use some other method of converting the string to a function or is there a way to override the wrapper within the new Function() syntax?
If you don't know the function name in advance, you can wrap the function definition in brackets to call it.
let response = `async function test() {
console.log("function called");
}`;
let executable = new Function(`(${response})();`);
executable();
If you need to pass it arguments or await it, make it return the function and call the function to get your actual function.
let func = `async function sum(a,b) {
return new Promise(resolve => setTimeout(() => resolve(a+b), 1000));
}`;
let executable = new Function(`return ${func};`)();
(async () => {
let val = await executable(3,4);
console.log("the sum is", val);
})();
If you know that it is guaranteed to be a function you could directly invoke it in the Function:
let data = 'function(arg1, arg2) { return arg1 + " " + arg2 }'
let func = new Function(`return (${data}).apply(this, arguments)`)
console.log(func(1,2))
With .apply(this, arguments) you call that function and pass the arguments you pass to your Function object into the received function. And the return returns the result that function.
Passing this to apply ensures that the Function object could be stored in an object and that the function your received could access that object using this. This might not be required, but makes the function behave like a regular function:
let data = 'function(arg1, arg2) { return arg1 + " " + arg2 + " " + this.prop }'
let obj = {prop : 'somevalue'}
obj.func = new Function(`return (${data}).apply(this, arguments)`)
console.log(obj.func(1, 2))
Simply use eval instead of new Function. You need to force the code to be an expression, not a statement, that's what the 0, part is for.
code = `function test() {
console.log('hey!')
}`
let executable = eval('0,' + code)
executable()
var executable = new Function(response + ';highlightCells()')
executable();
Since the returned code is a complete executable statement, use eval() to execute it, not new Function().
eval(response);
highlightCells();
Note that this requires that you know the name of the function that's being defined in the response. If not, you need to write code that parses it to extract the function name.

How to pass optional parameter to a function in react and javascript?

i want to set an optional parameter to a function using javascript. i have a function defined like below,
const function1 = () => {
setValue(!open);
};
i call it like below in other function
const main_function = () => {
function1();
}
now i want to pass an optional parameter to function1 to accept an optional parameter that sets it to true or false instead of toggling based on previous value.
how can i fix this. could someone help me with this thanks.
I hope this helps someone, you can just set the value to null like code below:
const function1 = ({arg1=null, arg2= false, arg3, ...}) => {
}
Pass a default value like so:
function multiply(a, b = 1) {
return a * b
}
multiply(5, 2) // 10
multiply(5) // 5
Example taken from
MDN docs
You can check the if the argument is defined or not, like:
const function1 = (arg) => {
setValue(arg === undefined ? !open : arg);
};
As far as I can understand you want something like this.
const function1 = (param) => {
setValue(params ? true : false);
}
It will set true if param is available otherwise false.
Correct me if I'm wrong.
we can't make function arguments optional in javascript directly. However, we can assign them a default value, in case no arguments is passed through. this is how you do it-
const abc = (arg1, arg2= 'default value') => {
//arg1 is required. however if arg2 is not passed in any case, the default value //will be used in place.
}

NodeJS output is function instead of printed strings

I am having trouble printing the correct result in NodeJS, why isn't my code printing the strings in the correct way ? I feel like console.log is not called at all. Why do I get :
[Function]
[Function]
[Function]
[Function]
Expected result:
Tigrou (buddy of Spider) was here!
Spider (buddy of Tigrou) was also here!
Tigrou (buddy of Spider) are in a boat...
1 (buddy of 2)3
The code I thought would work:
function build_sentence(...args)
{
var i = 0
var s = ""
for (let arg of args)
{
if (i == 1)
{
i++
s += "(buddy of " + arg + ") "
}
else
{
s += arg + " "
i++
}
}
return s
}
function mycurry(build_sentence)
{
return function(...args)
{
if (!args)
{
return build_sentence();
}
else
{
return mycurry(build_sentence.bind(this, ...args))
}
}
}
const curried = mycurry(build_sentence);
console.log(curried("Tigrou")("Spider")(" was here!"))
console.log(curried("Spider")("Tigrou")(" was also here!"))
console.log(curried("Tigrou", "Spider", " are in a boat... "))
console.log(curried(1)(2, 3))
Here's a full working solution for you (except the string spacing)
const build_sentence_n_params = 3;
function build_sentence(...args)
{
var i = 0
var s = ""
for (let arg of args)
{
if (i == 1)
{
i++
s += "(buddy of " + arg + ") "
}
else
{
s += arg + " "
i++
}
}
return s
}
// A generic n-parameter curry helper, that returns fn return value
// after n-parameters given, and continues currying with more parameters.
// Note that in this configuration, the caller would also need
// to know how many parameters to give before a value is returned.
function ncurry(fn, n)
{
// Note that we have to return an outer function here, rather than just
// returning `curry` function directly, so `params` is unique for each
// initial call to `curried`.
return function(...args) {
// Initial arguments (note: we can use this array without copy, since
// rest params will always be a new array)
const params = args;
// This function contains the return logic without having to duplicate it below
function next() {
// If we have all params, call the given function, otherwise
// return the curry function again for more parameters
return !n || params.length >= n ? fn.call(null, ...params) : curry;
}
// The outer function is only called once, but this curry
// function will be called for each additional time.
function curry(...args) {
// Accumulate additional arguments
params.push(...args)
return next();
};
return next();
};
}
function mycurry(build_sentence)
{
// Call the generic n-parameter curry helper to generate
// a specific curry function for our purposes.
return ncurry(build_sentence, build_sentence_n_params);
}
const curried = mycurry(build_sentence);
console.log(curried("Tigrou")("Spider")(" was here!"))
console.log(curried("Spider")("Tigrou")(" was also here!"))
console.log(curried("Tigrou", "Spider", " are in a boat... "))
console.log(curried(1)(2, 3))
Your method invocations are quite confusing so I took the liberty to simplify your code.
If you run the following:
const curried = build_sentence("Tigrou", "Spider", " was here!");
console.log(curried);
You will get your desired output:
Tigrou (buddy of Spider) was here!
Why you are using the mycurry method is beyond my understanding.
When debugging your code, the sentence is already built on the first invocation, and what happens is that the subsequent invocations are not really invocations of the inner functions.
the main issue about our code is that !args is all the time false, as [] to boolean is also true.
you have to use !args.length. however you should add an extra call to your curried function usage like curried(1)(2, 3)().
the other approach is using comparison of curried function number of required params (build_sentence.length) and the number of params passed (args.length), but it's not working with spread scenario.
upd:
ex.1 - using Function.prototype.length property
const curry = fn =>
(...args) =>
fn.length <= args.length
? fn(...args)
: curry(fn.bind(this, ...args));
const func1 = (a, b, c) => `${a},${b},${c}`;
const func2 = (...args) => args.join(',');
console.log(curry(func1)(1,3)(4));
console.log(curry(func1)(1,3,4));
console.log(curry(func1)(1)(3)(4));
console.log(curry(func2)(1));
in this case currying will work fine for func1 (function that has enumerable number of arguments) because func1.length = 3. however for func2 - func2.length = 0, so (curry(func1)) will be executed after first call.
ex.2 - using number of arguments passed
const curry = fn =>
(...args) =>
!args.length
? fn()
: curry(fn.bind(this, ...args));
const func = (...args) => args.join(',');
console.log(curry(func)(1)(2,3,4)(5)());
in this case function currying will only return result of fn executing, when called with no arguments. however it will handle innumerable arguments of curried function properly.

Confusion with how thenable callback works in Promise?

I am new to JS and was learning promises. The code excerpt I want to show is this
promisedFunction.then(data=>console.log(data))
or simply
promisedFunction.then(console.log)
which is equivalent of the former code excerpt. The question is how is that possible to just use then(console.log) instead of then(data=>console.log(data))? Does it mean that we can omit the passed-from-promise data in thenable callback?
data=>console.log(data) is a function that takes a parameter and calls a method with the passed in argument.
If you pass console.log it will execute the same method and still pass the same argument.
In effect you are dropping the extra function call - slightly more abstractly, imagine this:
//some function `f`
const f = x => x + 1;
//different function `g` that just forwards the call to `f`:
const g = x => f(x);
console.log(g(41)); //42
The definition of g is just a function that all it does is call f. Which has exactly the same effect as the f function. So, we can just re-write it as:
//some function `f`
const f = x => x + 1;
//different function `g` that just forwards the call to `f`:
const g = f;
console.log(g(41)); //42
and get exactly the same effect.
In Lambda Calculus, this removal of an essentially empty "wrapper function" is called Eta reduction.
Therefore, yes, both .then(data => console.log(data)) and .then(console.log) do exactly the same, you're performing the same sort of conversion where you're taking out a dummy forwarding function.
However, that's not always an option. For example, if removing the forwarding function will end up calling the target with more parameters, you can get different behaviour. An infamous example is parseInt when used in .map:
const arrayOfString = ["1", "2", "3"];
const arrayOfIntegers1 = arrayOfString.map(parseInt);
const arrayOfIntegers2 = arrayOfString.map(x => parseInt(x));
console.log("arrayOfIntegers1", arrayOfIntegers1);
console.log("arrayOfIntegers2", arrayOfIntegers2);
The issue is that Array#map calls the callback with three parameters - the item, the index, and the array. And parseInt has an optional second parameter - if passed in, it changes how the number is parsed. So you get NaN.
The easiest way to observe this is with console.log:
const arrayOfString = ["1", "2", "3"];
console.log(" --- using just `console.log` as callback ---");
arrayOfString.map(console.log);
console.log(" --- using just `x => console.log(x)` as callback ---");
const arrayOfIntegers2 = arrayOfString.map(x => console.log(x));
So, dropping the intermediate function works as long as it has the same arity as what it will be called with and what the next function will be called with.
data => console.log(data) is a function which takes in data and then calls the function console.log with data as its argument. So, it's a function which gets given data and then it gives that exact same data argument to the console.log function.
As you know console.log is a function, so, you can place console.log instead of your arrow function. This way, it will be called with the data argument and any other arguments natively passed into .then()'s onFulfilled callback (in this case it only gets given the resolved value of the promise).
Take the following example below. bar gets given "hello", which it gives to foo, foo accepts "hello" and returns "hello" back to where it was called, so, bar ends up returning "hello":
const foo = x => x;
const bar = x => foo(x);
console.log(bar("hello"));
In the above example, bar acts somewhat like a middle-man, as all it does is pass x to foo. This middle-step can be removed, by straight-up assigning bar to foo. This way, when we call bar(), we are executing the code defined within the function foo. This is the same idea that allows you to remove the need to explicitly defined your arrow function in .then():
const foo = x => x;
const bar = foo;
console.log(bar("hello"));
This style of coding is known as point-free style code and, in this case, is achieved by eta-reduction.
The return value of promise or the "resolved value" is the input to callback passed in then.
This behavior is not specific to promise but even [1,2,3].forEach(console.log) behaves the same where the 3 arguments of forEach are passed to console log. Thus you get ,
1 0 Array(3) [ 1, 2, 3 ]
2 1 Array(3) [ 1, 2, 3 ]
3 2 Array(3) [ 1, 2, 3 ]
promisedFunction.then expects a function to which will receive the data as a parameter.
in the case of data=>console.log(data) is an array function equal to:
function(data) {
console.log(data);
}
and console.log is also a function which prints in console what receives as a parameter.
That's why console.log works.
If you had:
var print = function(data) {
console.log(data);
}
promisedFunction.then(print) would also work.
See this working example that will resolve after 2 secs:
const doSomething = function(){
return new Promise((resolve, reject) => {
let wait = setTimeout(() => {
resolve('Promise resolved!');
}, 2000)
})
};
var print = function(data) {
console.log(data);
}
doSomething().then(print)

how can I arrange these functions to be high-order

Rather than having the following 2 string functions and calling them both
removeTrailingSlash = (site) => site.replace(/\/$/, "");
getLastPartOfURL = (url) => url.substr(url.lastIndexOf('/') + 1);
I was hoping to be able to combine them into one high-order function definition
removeTrailingSlash = (site) => site.replace(/\/$/, "");
getLastPartOfURL = site => removeTrailingSlash => url => url.substr(url.lastIndexOf('/') + 1);
So I guess I am unclear how i can use arrow functions to do this or if there is a more elegant approach. Thanks.
A higher order function is a function that takes a function as an argument and/or returns a function.
This answer assumes you want to understand higher-order functions more (hence the title) and are not just looking for a better way to get the last part of a path (because this probably isn't it).
In your second example:
getLastPartOfURL = site => removeTrailingSlash => url => url.substr(url.lastIndexOf('/') + 1);
I read that getLastPartOfURL will be a function takes an argument site and returns a function that takes and argument removeTrailingSlash that returns a function that takes an argument url and then returns a substring of that url. Notice, that most of the arguments you passed in to these function (site, removeTrailingSlash) were not used.
For an arrangement like this, you can instead pass actual functions into those arguments and then compose the functions at the end for your final result.
For example:
//define a few simple functions
const removeTrailingSlash = (url) => url.replace(/\/$/, "");
const getLast = (url) => url.substr(url.lastIndexOf('/') +1 )
// define a higher order function that expects functions as arguments and composes them
const getLastPartOfURL = removeTrailingSlashFN => getLastFN => url => getLastFN(removeTrailingSlash(url))
// call it by passing in functions and url:
let result = getLastPartOfURL(removeTrailingSlash)(getLast)("http://www.example.com/test/")
console.log(result)
/* Notice that this arrangement allows you to make function partials! */
let partial = getLastPartOfURL(removeTrailingSlash)(getLast)
// now you only need to pass in url
console.log(partial("http://www.example.com/foo/"))
console.log(partial("http://www.example.com/bar/"))
There are a lot of other patterns, but hopefully this helps.
You already have a decent answer about higher-order functions, but I think the concept you might actually be looking for is function composition. Composition lets you define a new function that is the result of passing the result of one function into the input of another, like this:
const compose = (f, g) => x => f(g(x));
// Your original functions
const removeTrailingSlash = url => url.replace(/\/$/, "");
const getLast = url => url.substr(url.lastIndexOf('/') + 1);
const both = compose(getLast, removeTrailingSlash);
console.log(both('http://www.example.com/foo/'));
console.log(both('http://www.example.com/bar/'));
// You can even do another composition for logging
const logBoth = compose(console.log, both);
logBoth('http://www.example.com/foo/');
logBoth('http://www.example.com/bar/');
Note in this particular case, it is compose that is actually the higher order function. It takes two functions f and g as parameters and returns a new function as a result. The new combined function, both, isn't itself considered a higher-order function, even though it was created using one. It takes a string as a parameter and returns a string.
Also note that while you can define compose yourself as I did above, it is general purpose enough to be already included in libraries like Ramda.
I'm assuming that you're expecting to get an output of bar from an input of something like https://google.com/foo/bar/.
If that is the case then I recommend something like this.
url.split('/').filter(Boolean).pop();
This will split up your url into an array based on where the slashes are (in the example case it will be ['https:', '', 'google.com', 'foo', 'bar', '']), then it will filter out all of the empty strings, and then return the last item in the array (bar)
If you really want them to be different functions then I recommend something like Method chaining so you'll be able to do something like:
I am guessing you need a function that gets the last part of the url that ends with /.
const getLastPart = url => url.match(/\/(\w*)\/$/)[1];
const p = getLastPart('part1/part2/part3/');
console.log(p);
I suspect1 you just want getLastPartOfURL to call removeTrailingSlash before it returns:
const removeTrailingSlash = (url) => url.replace(/\/$/, '');
const getLastPartOfURL = (url) => {
const noTrailing = removeTrailingSlash(url); // call removeTrailingSlash
return noTrailing.substr(noTrailing.lastIndexOf('/') + 1);
};
const tests = [
'http://www.example.com',
'www.example.com/foo',
'http://www.example.com/foo/bar',
'http://www.example.com/foo/bar/',
'www.example.com/foo/index.html',
];
console.log(JSON.stringify(tests.map(getLastPartOfURL)));
1: A Higher-Order Function is a function that takes a function as a parameter, or returns a function as its output.

Categories