How to spread an object to a function as arguments? - javascript

I have an object an a function which accept arguments, I would like to spread the objects so each property is an argument in that function.
What am I doing wrong in my code?
const args = {
a: 1
b: 2
}
const fn = (a, b) => a + b
// i am trying with no success
console.log(fn(...args))

Although the other answers are correct, they change the function signature to accept an object instead of 2 separate arguments. Here is how to use an object's values as function arguments without altering the function's signature. This requires Object.values (ES 2017) and the spread operator to be available in your runtime.
const args = {
a: 1,
b: 2
}
const fn = (a, b) => a + b
fn(...Object.values(args));
Keep in mind this will work only in your specific case, since Object.values returns the values of all object keys and doesn't guarantee alphabetical sort order. If you want to take only the values of properties which are named a and b, you can map over Object.keys(args) and filter only those values.

You can use ES6 object destructuring on passed parameter and then just pass your object.
const args = {a: 1, b: 2}
const fn = ({a, b}) => a + b
console.log(fn(args))
You can also set default values for those properties.
const args = {b: 2}
const fn = ({a = 0, b = 0}) => a + b
console.log(fn(args))

You need to do it like this
const fn = ({a, b}) => a + b

The other answers are certainly applicable in particular situations, still have some limitations as well. Therefore I'd like to propose a different approach. The idea is to add to the object a method that returns an array of desired parameters in the appropriate order. That method is executed when passed to target function as argument and result destructured with spread operator.
const args = {
a: 1,
b: 2,
argumentify: function () {
return [this.a, this.b];
}
};
const fn = (a, b) => a + b;
console.log(fn(...args.argumentify()));
Benefits of this approach:
1) Does not require changes of the target function's signature, so can be used to ANY function.
2) Guarantees correct order of parameters (which is (as I understand) not guaranteed when spreading object).
3) Can itself be parametrized if needed.

Turn the args to an array should work:
const args = [1, 2]
const fn = (a, b) => a + b
console.log(fn(...args))
See Replace apply() for details.

Related

Why arguments is given ...arguments in the arguments place like this?

My point is this:
function sum(...arguments) {
if (arguments.length === 1) {
const [firstArg] = arguments;
if (firstArg instanceof Array) {
return sum(...firstArg)
}
}
return arguments.reduce((a, b) => a + b);
}
What is this ... are doing infront of arguments. And also please help me know that why const [firstArg] is given and how this is working. And please explain me instanceof in easy words. My native language is not english. Thank you so much.
That's the rest syntax:
The rest parameter syntax allows a function to accept an indefinite number of arguments as an array.
This:
const [firstArg] = arguments;
is destructuring assignment:
[it] makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Re instanceOf:
The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value.
Unpacking this code:
function sum(...arguments) {
// arguments is an array thanks
// to the rest syntax. If it has a length of
// one...
if (arguments.length === 1) {
// Get the first element
const [firstArg] = arguments;
// If that element is an array
if (firstArg instanceof Array) {
// Call sum again with that element
return sum(...firstArg)
}
}
// Return the sum of the arguments
return arguments.reduce((a, b) => a + b);
}
Your function could be easily simplified by flattening the arguments, and then using reduce to return the sum. And this way you can even have a set of single values passed in, or n multiple arrays as a single argument to the function, or a mixture of both.
function sum(...args) {
return args.flat().reduce((acc, c) => acc + c, 0);
}
console.log(sum(1, 2, 3));
console.log(sum([1], 1, 2, 3));
console.log(sum([1, 2, 3]));
console.log(sum([1, 2], [1, 4, 3]));
console.log(sum([1, 2], [12, 20], [1, 4, 3]));
(...arguments) is the rest parameter syntax and allows a function to accept an indefinite number of arguments as an array, providing a way to represent variadic functions in JavaScript.
for more information, you can read here
const [firstArg] = arguments;
is something named Destructuring assignment
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
you can read more, here
and in the last, The instanceof operator tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value
This because ... is a javascript tool that allows you to put multiple parameters in a function it is called the rest parameter function.

How to use initial value when composing functions [duplicate]

I'm building a pipe with Ramda.js which accepts three arguments. The first function needs those three arguments, and it's result is used in the second function. However, the second function also needs one of the initial arguments. I cannot figure out the branching to build something like it.
In pseudocode style, I need something like this:
const composedFunction = R.pipe(
firstFunction,
secondFunction,
);
const firstFunction = (reusedArgument, secondArgument, thirdArgument) => someAnswer;
const secondFunction = (reusedArgument, someAnswer);
console.log(composedFunction({ foo: bar }, [5, 3, 4], [100, 12, 12]));
I can think of a few solutions:
Wrap your pipe inside another function so that functions in your composition can still refer to the original parameters.
Here func2 accepts the output of func1 but also has access to the initial b parameter. Obviously func2 must be curried and be designed to accept its "data" as the last parameter (which is a tenet of Ramda and functional programming in general I'd say).
const func3 = (a, b, c) =>
pipe(func1, func2(b))
(a, b, c);
func3(10, 20, 30);
Other option, func1 returns an array which you can destructure in func2.
I don't think this is particularly nice but it is an option:
const func1 = (a, b, c) => [a + c, b];
const func2 = ([sum, b]) => sum * b;
const func3 = pipe(func1, func2);
func3(10, 20, 30);
I think the simplest thing here is to not bother with Ramda's pipe function, which is not designed to handle such case, and just write it manually:
const func1 = (a, b, c) => `func1 (${a}, ${b}, ${c})`
const func2 = (a, d) => `func2 (${a}, ${d})`
const func3 = (a, b, c) => func2 (func1 (a, b, c), a)
console .log (func3 ('a', 'b', 'c'))
Ramda has recently been considering a way to make this easier for longer pipelines; even with that, though, the above is probably simpler for just a few functions.

Can I bypass optional parameters and still set a rest parameter in Javascript?

I have a function with a required parameter (A), some optional parameters (B,C) and a rest parameter (Z)
const doTheThing = (a, b = 'B', c = 'C', ...z) => {
console.log(a, b, c, z);
}
I have cases where I would like to call the function without specifying the optional parameters, but still specify the rest parameter "Z"
doTheThing('A', ...'Z');
Expected Output:
'A', 'B', 'C', 'Z'
Unfortunately, I get the following:
Parsing error: Shorthand property assignments are valid only in destructuring patterns
How do I go about solving this?
JavaScript doesn't allow supplying named parameters or any sort of parameter skipping, so it's not possible to do what you want with the function in its current form. Here are some alternatives, though:
Plain JavaScript approach: a configuration Object as parameter
Instead of accepting multiple parameters
func = (a, b, c) => { /* operate with parameters */ }
func("One", "Two", "Three")
your function will instead accept an object
func = config => { /* operate with config */ }
func({a: "One", b: "Two", c: "Three"})
This is a common pattern in JavaScript because it allows you to almost name your variables and doesn't require you pass them in the correct order.. It makes it easy to pass a large quantity of them and it can also make it easy to default them, too.
const doTheThing = (config) => {
const defaultProperties = {
b: "B",
c: "C"
}
const {a, b, c, ...rest} = Object.assign({}, defaultProperties, config);
const z = Object.values(rest); //extract their values, otherwise you get an object
console.log(a, b, c, z);
}
doTheThing({a: "A", x: "X", y: "Y", z: "Z"});
It is slightly clunky to use with rest parameters but not unworkable.
However, it does mean that it may be harder to see what parameters you can pass and what is required, if you have a large number of them.
Object Oriented approach: Builder pattern
You create a builder object - it serves to hold values until you call the final method at which point it takes all parameters and constructs an object in one go.
This is how more Object Oriented languages handle having a multitude of parameters where you can even have some of them optional. It's not really common to see builders defined like this in JavaScript but it's not too strange, either. If you use classes already or even TypeScript, then this is probably a better fit.
class DoTheThingBuilder {
constructor() {
this.a = null;
this.b = "B";
this.c = "C";
this.z = null;
}
withA(a) {
this.a = a;
return this;
}
withB(b) {
this.b = b;
return this;
}
withC(c) {
this.c = c;
return this;
}
withEverythingElse(...z) {
this.z = z;
return this;
}
doTheActualThing() {
const {a, b, c, z} = this;
console.log(a, b, c, z);
}
}
const builder = new DoTheThingBuilder();
builder
.withA("A")
.withEverythingElse("X", "Y", "Z")
.doTheActualThing();
As you can see, this can be pretty verbose for some simple tasks. It is a big overkill for this example, but perhaps in actual usage, you might find it helps.
I've deviated a bit from the usual approach - normally, you would set all parameters needed with the builder and finally call .build() which constructs an object. In this case, I basically renamed build to doTheActualThing and it's executing the function.
Functional approach: Currying
The concept of currying is quite simple - instead of having one function that accepts several parameters
func = (a, b, c) => { /* operate with parameters */ }
you have a function that takes one parameter, that returns a function that takes the second parameter, that returns another function, etc., until all parameters are satisfied, at which point the full function is executed.
func = a => b => c => { /* operate with parameters */ }
In many ways, this is the functional equivalent of the OO Builder pattern.
const doTheThing = (a) =>
(b = "B") =>
(c = 'C') =>
(...z) => console.log(a, b, c, z);
doTheThing("A")()()("X", "Y", "Z");
This way you can skip the second and third parameter by not supplying them and you'd get the defaults. It's also way shorter than a builder. However, reading the function can be a bit weird.
That is not possible and very error-prone. The point of naming your parameters is to know what they are and in what order they are coming.
You could achieve something similar using object as a function parameter:
const doTheThing = ({ a, b = "B", c = "C", others = {} }) => {
const params = { a, b, c, ...others }; // this will merge your parameters into one object
console.log(params);
}
doTheThing({ a: "A", others: { z: "Z" }});
This will log A, B, C, Z. Demo: https://codepen.io/tomekbuszewski/pen/jQqmNL?editors=0011

Parameter destructuring (equivalent of python's double-splat)

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})

Understanding the Context of Curly Brackets '{}'

I have been reviewing other people's code and while ES2015 on the whole is taking some getting use to, however, I keep on getting stuck with Destructuring.
Previously, In Javascript, the Curly Brackets {} were either used for blocks or objects. e.g.
// Curly Brackets Block
If () {
...
}
// Curly Brackets in Obj
var obj = {
a : 1,
...
}
However, in destructuring, we see again and again the following syntax:
let a = ({ a, b }) => {
}
My question, is the arguments container an actual object or just a block?
Please explain whether the following be the same as the code above:
let a = ( a, b ) => {
}
EDIT: My understanding (so far) from reading Axel Rauschmayers article on Destruturing is that we are merely mapping the props. into a new Obj always? I.e:
let a = { first : a, second : b } = AnObj;
===
a.a === AnObj.first;
a.b === AnObj.second;
Is the above correct? Is an obj always instantiated? However, that doesn't make sense as in the above function, the object thus created for the props would be an anonymous object, right?
Many thanks,
No, the curly braces in destructuring do form neither a block nor an object literal.
They definitely are not a block because they are not a statement (and don't contain a statement list), they are an expression like an object literal. In fact they even do have the same syntax as an object literal, the only difference is that they are in the position of an assignment target (left hand side of an assignment operator) or a function parameter.
Is let a = ({ a, b }) => {…} the same as let a = ( a, b ) => {…}?
No, really not. Both parameter lists do declare variables a and b for the function scope, but the first function expects an object with properties .a and .b while the second function expects two arguments.
My understanding is that we are merely mapping the properties into a new obj?
No. There is no new object created/instantiated. There is only the object that you pass in (the right hand side). And it is destructured - "pulled apart" - into pieces that are then assigned to the various sub-targets (variables, property references).
To write
a.b = anObj.first;
a.c = anObj.second;
with a destructuring assignment you'd use
({first: a.b, second: a.c}) = anObj;
(the parenthesis are necessary to distinguish the expression from a block).
The more common use case is for variable initialisations however. You can shorten
let b = anObj.first,
c = anObj.second;
to
let {first: b, second: c} = anObj;
And also there's a shorthand when the variable has the same name as the property, so
let first = anObj.first,
second = anObj.second;
is equivalent to
let {first, second} = anObj;
Is let a = { first : a, second : b } = anObj; correct?
No, that doesn't make much sense. It would desugar to
let a;
a = anObj.first;
b = anObj.second;
a = anObj;
It is for destructuring:
var obj = {a: 1, b: 2},
add = ({a, b}) => a + b;
console.log(add(obj)); //3
So basically to the statements inside the function it would appear there are 2 arguments, but when you call it you only pass an object.

Categories