In python I can pass a dict whose keys match parameters' names with the ** (double-splat) operator:
def foo(a, b):
print (a - b)
args = {'b': 7, 'a': 10}
foo(**args) # prints 3
How to do the same in ES6? This doesn't work:
function foo(a, b) {
console.log(a - b)
}
args = {b: 7, a: 10}
foo(...args)
NB: I'm looking for a solution that wouldn't involve changing the signature of foo because I want it to be used either way (with and without destructuring). So the following should work:
foo(<magic>args);
foo(123, 456);
Bonus question: why is the error message "undefined is not a function"? What exactly is undefined here?
(As answered by #Nina Scholz in the comments, this is because ... requires its argument to have Symbol.iterator, which is not defined for objects).
How to do the same in ES6?
There are no named arguments in JS, only positional ones. So the answer is: you can not.
What you can do is either emulate named arguments via object passing, as #Andy suggested.
function foo({ a, b }) {
console.log(a - b);
}
let args = { b: 7, a: 10 };
foo(args);
Or you could make args to be an array, so you can destruct it into positional arguments.
function foo(a, b) {
console.log(a - b);
}
let args = [10, 7];
foo(...args);
Okay-okay, just for the sake of the argument: it is possible to write a function that will extract parameters of foo and yield properties of args in required order.
function * yolo(args, fn) {
const names = fn.toString().match(/\(.+\)/)[0]
.slice(1, -1).split(',')
.map(x => x.trim());
while (names.length) {
yield args[names.shift()];
}
}
function foo(a, b) {
console.log(a - b);
}
const args = { b: 7, a: 10 };
foo(...yolo(args, foo));
I would not dare to use it in production though.
You need to wrap your args in curly braces, and again in the argument list for the function.
function foo({a, b}) {
console.log(a - b)
}
let args = {b: 7, a: 10}
foo({...args})
Related
Is there any way or pattern that would allow me to intercept destructured function arguments and do something with them before the function is executed, without knowing the argument signature beforehand? I.e. something like:
const add = ({a, b}) => a + b
const multiplyArgs = (fun) => (...args) => fun(...args.map(x => x * 10)) // Won't work
const res = multiplyArgs(add)({a: 5, b: 10, c: 20}) // 150, won't perform the multiplication on c
The only option I found so far is using a regex to get the arguments from the string representation of the function, but that's very messy.
EDIT:
The actual use case is this:
I have an object with RxJS observables/subjects, and would like to be able to call a function/method that would take in another function, pick the required observables from the object, combine them, and then pipe the function to the new combined observable.
const observablePool = {a: new Rx.BehaviorSubject(5), b: new Rx.BehaviorSubject(10)}
updatePool( ({a, b}) => ({c: a + b}) )
// In the background:
// const picked = {a: observablePool.a, b: observablePool.b}
// observablePool.c = Rx.combineLatest(picked)
// .pipe(Rx.map({a, b} => a + b))
The idea is to hide the implementation of accessing the observables object and creating new combined observables. The user should be able to chain simple functions whose results would get lifted into observables automatically.
I can do it by adding a pick() function, i.e. updatePool(pick("a", "b"), ({a, b}) => ({c: a + b}) ) but that duplicates and decouples the argument names. I was wondering if there was a more elegant solution.
For just one object argument, you could map over Object.entries.
const add = ({a, b}) => a + b;
const multiplyObj0 = fun => obj => fun(Object.fromEntries(Object.entries(obj)
.map(([k, v])=>[k, v * 10])));
const res = multiplyObj0(add)({a: 5, b: 10});
console.log(res);
Lets assume we have such function:
const func = (a, b, {c: {d}}) => {console.dir(c)}
How is this function should be called and what structure of 3rd parameter?
I tried a lot of variations, but always got an error: Cannot destructure propertydof 'undefined' or 'null'.
Thanks!
const func = (a, b, {c: {d}}) => {console.dir(d)}
func(null, null, {c: {d: document.location}});
This function has to be called with object that has key c, which has object with key d as value:
func(a, b, {c: {d: document.location }})
console.dir() takes any JS object as parameter.
{ c: {d}} is a syntax called object destructuring and its purpose in this context is unpacking fields from objects passed as function parameter.
{d} is shorter syntax for object with key d and value of variable d ({d: d}).
To unpack variable d from object under key c, that object have to have that key initialized! But when you further destructurize the object passed as argument, you don't have that object as a variable in the scope.
In example you have provided, you will not be able to access object c, as it has been destructurized and only object d is available. Either you have mistake in your code or you need something like Anurat Chapanond has posted.
Here is one example.
const func = (a, b, {c: {d}}) => {console.dir(c)},
c = { d: 'hello' }
func(1, 2, { c })
I define c as an object with a property d which is a string 'hello'.
when func is called, the third parameter I pass to the function is an object with a property c.
{ c } is a shorthand for { c: c }
The below is a function fn where expected result is for a, b, c to defined at every call of fn, whether an object parameter is passed or not. If object is passed which sets property, property should be set only for that object.
const fn = (opts = {a:1, b:2, c:3}) => console.log(opts);
when called without parameters the result is
fn() // {a: 1, b: 2, c: 3}
when called with parameter, for example {b:7}, the expected result is
fn({b:7}) // {a: 1, b: 7, c: 3}
however, the actual result is
fn({b:7}) // {b: 7}
Was able to get expected result by defining an object outside of function and using Object.assign() within function body
const settings = {a: 1, b: 2, c: 3};
const fn = opts => {opts = Object.assign({}, settings, opts); console.log(opts)}
fn({b: 7}) // {a: 1, b: 7, c: 3}
fn(); // {a: 1, b: 2, c: 3}
/*
// does not log error; does not return expected result
const fn = (opts = Object.assign({}, settings, opts)) => console.log(opts)
*/
Can the above result be achieved solely utilizing default parameters, without defining an object to reference outside of function parameters or within function body?
Maybe I misunderstood the question, but you seem to be looking for default initialisers for each separate property. For that, you have to use destructuring:
const fn = ({a = 1, b = 2, c = 3} = {}) => console.log({a, b, c});
If you want to keep arbitrary properties, not just those that you know of up front, you might be interested in the object rest/spread properties proposal that allows you to write
const fn = ({a = 1, b = 2, c = 3, ...opts} = {}) => console.log({a, b, c, ...opts});
Can an opts variable as the single object reference be achieved solely utilizing default parameters, without defining an object to reference outside of function parameters or within function body?
No. Parameter declarations are only able to initialise variables with (parts of) the arguments, and possibly (as syntactic sugar) with default values when no or undefined argument (parts) are passed. They are not able to carry out unconditional computations and create variables inialised from the results - which is what you attempt to achieve here.
You are supposed to use the function body for that.
No
The best that can be done is either your own answer or this:
const fn = (default_parameters) => {
default_parameters = Object.assign({}, {a: 1, b: 2, c: 3},default_parameters);
console.log('These are the parameters:');
console.log(default_parameters);
}
fn();
fn({b: 7});
fn({g: 9, x: 10});
The default parameter block is only executed if the value is not set, so your own answer is the best that is on offer ie use two parameters
You can convince yourself of this by creating a code block that will fail if executed and testing that passing a parameter works (to show that the code block is not executed) and testing that not passing a parameter fails (showing that the code block is only executed when no parameter is passed).
This should demonstrate clearly that any paramter passed will prevent the default parameter from being evaluated at all.
const fn = (default_parameters = (default_parameters = Object.assign({}, {a: 1, b: 2, c: 3},default_parameters))) => {
console.log('These are the parameters:');
console.log(default_parameters);
}
fn({b: 7});
fn();
fn({g: 9, x: 10});
We can set fn as a variable which returns an arrow function expression. When called set a, b, c and rest parameters reference using spread element at new object, which is returned when the function is invoked.
const fn = ((...opts) => ({a:1,b:2,c:3, ...opts.pop()}));
let opts = fn();
console.log(opts);
opts = fn({b: 7});
console.log(opts);
opts = fn({g: 9, x: 10});
console.log(opts);
Using rest element, Object.assign(), spread element, Array.prototype.map(), setting element that is not an object as value of property reflecting index of element in array.
const fn = ((...opts) => Object.assign({a:1,b:2,c:3}, ...opts.map((prop, index) =>
prop && typeof prop === "object" && !Array.isArray(prop)
? prop
: {[index]:prop}))
);
let opts = fn([2,3], ...[44, "a", {b:7}, {g:8, z: 9}, null, void 0]);
console.log(opts);
Though code at OP uses single default parameter, until we locate or develop a procedure for using only single parameter, we can utilize setting two default parameters to achieve expected result.
The first parameter defaults to a plain object, at second default parameter we pass parameter identifier from first parameter to Object.assign() following pattern at Question.
We reference second parameter identifier of function fn to get the default parameters when called without parameters; when called with first parameter having properties set to properties of object passed at first parameter and default parameters, the former overwriting the latter at the resulting object.
const fn = (__ = {}, opts = Object.assign({}, {a: 1, b: 2, c: 3}, __)) =>
console.log(opts);
fn();
fn({b: 7});
fn({g: 9, x: 10});
Given a function that takes a type a, I need to apply function z that returns a function that takes type b. Basically:
z -> f(a) -> f(b)
Here is a more specific example. This is a function that takes a string and returns a set of methods that use the string:
# f(a)
_typeCheck = (str) ->
isArray: -> str is TYPES.ARRAY
I want to transform this function into:
# f(b)
_typeCheck = ->
isArray: (str) -> str is TYPES.ARRAY
What is the function z that will do this? Please help.
NOTE
I'm trying to use a functional programming concept here. Haskell has functors that modify type constructors to take in different types. I believe a similar concept would help me solve my problem. Read more here
The first issue is having a function that takes a "type". JavaScript is loosely typed so any type enforcement will need to come from you.
That said, you can define a function that returns another function like this:
function factory(a) {
// check typeof a
return function(b) {
// do something with a
// check typeof b
};
}
// invoke
var myNewFunction = factory("a string");
myNewFunction("another string");
If you are looking for a function that returns a map of functions for checking types perhaps you could use a pattern like this:
function _typeCheck() {
return {
isArray: function(o) {
return Array.isArray(o);
},
isString: function(o) {
return typeof o === 'string';
},
...etc
};
}
console.log(_typeCheck().isArray(['one','two']));
console.log(_typeCheck().isString('foo'));
I don't know Haskell, but in JS you can construct something like this:
// define some functors
list = xs => a2b => xs.map(a2b);
tree = t => a2b =>
t ? {
x: a2b(t.x),
le: tree(t.le)(a2b),
ri: tree(t.ri)(a2b)
}
: null;
maybe = x => a2b => x ? a2b(x) : x;
// add five to any functor
add5 = f => f(x => x + 5);
// let's test
myTree = {
x: 1,
le: {
x: 2,
le: {x: 3},
ri: {x: 4}
},
ri: {x: 4}
};
myList = [10, 11, 12, 13];
log = x => document.write('<pre>' + JSON.stringify(x, 0, 3) + "</pre>");
log([
add5(maybe(22)),
add5(maybe(undefined)),
add5(list(myList)),
add5(tree(myTree))
]);
I use Python a lot, and I am just quickly learning JavaScript right now (or should I say re-learning). So, I wanted to ask, what is the equivalent of *args and **kwargs in JavaScript?
ES6 added a spread operator to JavaScript.
function choose(choice, ...availableChoices) {
return availableChoices[choice];
}
choose(2, "one", "two", "three", "four");
// returns "three"
The closest idiom for *args would be
function func (a, b /*, *args*/) {
var star_args = Array.prototype.slice.call (arguments, func.length);
/* now star_args[0] is the first undeclared argument */
}
taking advantage of the fact that Function.length is the number of arguments given in the function definition.
You could package this up in a little helper routine like
function get_star_args (func, args) {
return Array.prototype.slice.call (args, func.length);
}
and then do
function func (a, b /*, *args*/) {
var star_args = get_star_args (func, arguments);
/* now star_args[0] is the first undeclared argument */
}
If you're in the mood for syntactic sugar, write a function which transforms one function into another one which is called with required and optional arguments, and passes the required arguments along, with any additional optional arguments as an array in final position:
function argsify(fn){
return function(){
var args_in = Array.prototype.slice.call (arguments); //args called with
var required = args_in.slice (0,fn.length-1); //take first n
var optional = args_in.slice (fn.length-1); //take remaining optional
var args_out = required; //args to call with
args_out.push (optional); //with optionals as array
return fn.apply (0, args_out);
};
}
Use this as follows:
// original function
function myfunc (a, b, star_args) {
console.log (a, b, star_args[0]); // will display 1, 2, 3
}
// argsify it
var argsified_myfunc = argsify (myfunc);
// call argsified function
argsified_myfunc (1, 2, 3);
Then again, you could just skip all this mumbo jumbo if you are willing to ask the caller to pass the optional arguments as an array to start with:
myfunc (1, 2, [3]);
There is really no analogous solution for **kwargs, since JS has no keyword arguments. Instead, just ask the caller to pass the optional arguments in as an object:
function myfunc (a, b, starstar_kwargs) {
console.log (a, b, starstar_kwargs.x);
}
myfunc (1, 2, {x:3});
ES6 Update
For completeness, let me add that ES6 solves this problem with the rest parameter feature. See Javascript - '...' meaning
I found a good solution here:
http://readystate4.com/2008/08/17/javascript-argument-unpacking-converting-an-array-into-a-list-of-arguments/
Basically, use function.apply(obj, [args]) instead of function.call. apply takes an array as the 2nd arg and 'splats' it for you.
The nearest equivalent is the arguments pseudo-array.
ECMAScript 6 will have rest parameters which do the same thing as the splat operator.
For those who might be slightly lost about *args and **kwargs magic variables read http://book.pythontips.com/en/latest/args_and_kwargs.html
Summary:
*args and **kwargs are just conventional ways of writing the magic variables. You could just say * and ** or *var and **vars. That said lets talk about the JavaScript equivalent in 2019.
*args in python represents a JavaScript Array e.g. ["one", "two", "three"] to pass that into a python function you would just define the function as def function_name(*args): meaning this function accepts "arrays" or "list if you wish" to call that you would simply use function function_name(["one", "two", "three"]):
same thing in JavaScript can be done by using :
function func(x,y,z){
...
}
let args = ["one", "two", "three"];
func(...args)
**or more dynamically as**
function func(inputs<T>:Array){
for(index in inputs){
console.log(inputs[index]);
}
}
let args = ["one", "two", "three"];
func(args)
Have a look at https://codeburst.io/a-simple-guide-to-destructuring-and-es6-spread-operator-e02212af5831
**kwargs on the other hand represent just an array of key value pairs(objects) that's it. Thus
**kwargs e.g is [{"length": 1, "height": 2}, {"length":3, "height": 4}]
in python to define a function accepting array of objects you just say def function_name(**kwargs): then to call that you can do function_name( [{"length": 1, "height": 2}, {"length":3, "height": 4}]):
similarly in JS
const kwargs = [{"length": 1, "height": 2}, {"length":3, "height": 4}]
function func(obj1, obj2){
...
}
func(...kwargs);
**or more dynamically as:**
const kwargs = [{"length": 1, "height": 2}, {"length":3, "height": 4}]
function func(obj){
for(const [key, value] of Object.entries(obj)){
console.log(key, ": ", value)
}
func(kwargs);