Using named arguments for functions - javascript

Is there any downside to using named arguments for functions in JavaScript and PHP. I don't see any downside. There's the fact that you need to extract them (though you don't absolutely have to I guess) - but you can use that as a way of using defaults. Eg.
Javascript:
function myFunction (opt)
{
var name = opt.name || 'Barry',
age = opt.age || 26,
eyes = opt.eyes || 'brown';
}
And call it like this:
myFunction ({
name: 'Fred',
age: 56,
eyes: 'pink'
})
PHP
function myFunction ($opt)
{
$name = $opt['name'] ? $opt['name'] : 'Barry';
$age = $opt['age'] ? $opt['age'] : 26;
$eyes = $opt['eyes'] ? $opt['eyes'] : 'brown';
}
EDIT: Maybe use the extract() function here to make things easier.
And call it like this:
myFunction ([
'name' => 'Fred',
'age' => 56,
'eyes' => 'pink'
])

In Javascript, you could use default values, like
function myFunction({ name = 'Barry', age = 26, eyes = 'brown'} = {}) {
console.log(name, age, eyes);
}
myFunction();
myFunction({ name: undefined, age: 30 });
myFunction({ name: 'jane', age: 35, eyes: 'blue' });

Is there any downside to using named arguments for functions in JavaScript and PHP
Just one. Neither PHP nor JavaScript support named arguments.
EDIT
Here wiki page on what named arguments really is, as many seem to get the concept wrong.

You are talking about an options object, not named arguments. Named arguments are not available in JavaScript, and would look something like this following line. Note that the parameters are not inside an object:
myFunction( name: 'Fred', age: 56, eyes: 'pink' );
Using an options object rather than individual function parameters is useful if there are more than a few arguments to pass. The object doesn't have to worry about ordering, or whether elements are optional.
ES2015 supports destructuring and defaults, which can simplify this even more, though the formatting may look very different at first...
function myFunction ({ name = 'Barry', age = 26, eyes = 'brown'})
{
console.log(name, age, eyes);
}
This function will throw an error if an object is not passed, but it can accept an empty object, or one with any number of those keys. As Nina's answer shows, you would need another default to prevent that error:
function myFunction ({ name = 'Barry', age = 26, eyes = 'brown'} = {})
{
console.log(name, age, eyes);
}

Related

How can you define an objects properties as variables without knowing their names? [duplicate]

Let's suppose I have the following object:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
And that I want only the id and fullName.
I will do the following :
const { id, fullName } = user
Easy-peasy, right?
Now let's suppose that I want to do the destructuring based on the value of another variable called fields.
const fields = [ 'id', 'fullName' ]
Now my question is : How can I do destructuring based on an array of keys?
I shamelessly tried the following without success:
let {[{...fields}]} = user and let {[...fields]} = user. Is there any way that this could be done?
Thank you
It's not impossible to destructure with a dynamic key. To prevent the problem of creating dynamic variables (as Ginden mentioned) you need to provide aliases.
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
const {[fields[0]]: id, [fields[1]]: fullName} = user;
console.log(id); // 42
console.log(fullName); // { firstName: "John", lastName: "Doe" }
To get around the problem of having to define static aliases for dynamic values, you can assign to an object's dynamic properties. In this simple example, this is the same as reverting the whole destructuring, though :)
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
({[fields[0]]: object[fields[0]], [fields[1]]: object[fields[1]]} = user);
console.log(object.id); // 42
console.log(object.fullName); // { firstName: "John", lastName: "Doe" }
sources:
https://twitter.com/ydkjs/status/699845396084846592
https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch2.md#not-just-declarations
Paul Kögel's answer is great, but I wanted to give a simpler example for when you need only the value of a dynamic field but don't need to assign it to a dynamic key.
let obj = {x: 3, y: 6};
let dynamicField = 'x';
let {[dynamicField]: value} = obj;
console.log(value);
Short answer: it's impossible and it won't be possible.
Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.
Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.
As discussed before, you can't destruct into dynamically named variables in JavaScript without using eval.
But you can get a subset of the object dynamically, using reduce function as follows:
const destruct = (obj, ...keys) =>
keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const object = {
color: 'red',
size: 'big',
amount: 10,
};
const subset1 = destruct(object, 'color');
const subset2 = destruct(object, 'color', 'amount', 'size');
console.log(subset1);
console.log(subset2);
You can't destruct without knowing the name of the keys or using an alias for named variables
// you know the name of the keys
const { id, fullName } = user;
// use an alias for named variables
const { [fields[0]]: id, [fields[1]]: fullName } = user;
A solution is to use Array.reduce() to create an object with the dynamic keys like this:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName', 'age' ];
const obj = fields.reduce((acc, k) => ({ ...acc, ...(user.hasOwnProperty(k) && { [k]: user[k] }) }), {});
for(let k in obj) {
console.log(k, obj[k]);
}
I believe that the above answers are intellectual and valid because all are given by pro developers. :). But, I have found a small and effective solution to destructure any objects dynamically. you can destructure them in two ways. But both ways are has done the same action.
Ex:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
using "Object.key", "forEach" and "window" object.
Object.keys(user).forEach(l=>window[l]=user[l]);
Simply using Object. assign method.
Object.assign(window, user)
Output:
console.log(id, displayName, fullName)
// 42 jdoe {firstName: "John", lastName: "Doe"}
Anyway, I am a newbie in JS. So, don't take it as an offense if you found any misleading info in my answer.

Javascript Object destructuring and default parameters combined

Today I came across the following syntax which I didn't recognize:
const createUser = ({
age = 1,
name = 'Anonymous',
}) => ({
age,
name,
});
const defaultP = createUser({
age: 5
});
console.log(defaultP);
I think it uses Object destructuring and default parameters in order to set defaults of the object which is send as an argument.
The syntax threw me a bit off because normally I see object destructuring only in the following manner:
let obj = {
prop1: 1
}
const {prop1} = obj;
console.log(prop1);
Question:
How does this syntax work exactly?
That syntax indeed uses Object Destructuring in order to extract default values from the parameter object. There are some examples in the Mozilla documentation that helps us understand the trick, check this out:
var {a = 10, b = 5} = {a: 3};
console.log(a); // 3
console.log(b); // 5
A possible disadvantage of your example is that the createUser method ignores all other values of the parameter object and always returns an object that contains only age and name. If you want to make this more flexible, we could use Object.assign() like this:
const createUser = (o) => Object.assign({ age: 1, name: 'Anonymous' }, o);
In this case, the user created will be an object that merges the parameter object with the default values. Note now that the default values are in the method body. With this method we can create users that contain other properties, example:
const superman = createUser({ name: 'Superman', type: 'superhero' });
console.log(superman);
// output: {age: 1, name: "Superman", type: "Superhero"}
Your code is using both Object Destructuring and default function props.
const createUser = ({
age = 1,
name = 'Anonymous',
}) => ({
age,
name,
});
Here function createUser is accepting single argument of type Object. Function is returing same object, if you have both object properties defined in your argument, then it will return your passed object. Otherwise it will replace it with default values, which are 1 and Anonymous respectively.
You can further read about it here:
https://wesbos.com/destructuring-renaming/
https://wesbos.com/destructuring-default-values/
If you use babel and transpile your code to ES5, it will look like this:
function createUser(params) {
return {
age: typeof params.age === 'undefined' ? 1 : params.age,
name: typeof params.name === 'undefined' ? 'Anonymous' : params.name,
};
}
Just a note: default values for function arguments works the same way:
const multiply = (a, optionalB) => {
const b = typeof optionalB !== 'undefined' ? optionalB : 2;
return a * b;
}
Is same as:
const multiply = (a, b = 2) => {
return a * b;
}
It increases a readability, mostly in cases when argument is used several times.

what is the most terse way to return an array of entities in typescript?

I'm trying to figure out the most terse way to return an array of objects from a ts function. The following function works as expected:
getAuthors1(): Author[]
{
var authors: Author[] =
[
{
FirstName: "John";
MI: "J";
LastName: "Smith";
}
];
return authors;
}
The following function errors out b/c it appears that ts won't let me return an object array directly as opposed to as a variable:
getAuthors2(): Author[]
{
return Author[] =
[
{
FirstName: "John";
MI: "J";
LastName: "Smith";
}
];
}
The following function errors out b/c a value isn't provided for MI:
getAuthors3(): Author[]
{
var authors: Author[] =
[
{
FirstName: "John";
LastName: "Smith";
}
];
return authors;
}
Questions:
Are values required for all object properties when creating an object initialized array?
1a. If this is the case do developers typically initialize property values in the class?
Is there a way to return an object array directly, similar to my getAuthors2() example above, as opposed to having to assign this to a variable and then return the variable?
If you have an interface defined and you create an object where you tell TypeScript that it should be of that type, it will complain for any missing property, as it should.
One way to get around this is to use a mapped type where you state that each property of the object is optional, and use it like Partial< Author >. See the official documentation for more info.
You can return the array right away, just remove the type that you've added after return:
getAuthors2(): Author[] {
return [
{
FirstName: 'John',
MI: 'J',
LastName: 'Smith',
}
];
}
Also wherever possible you should remove the manually defined types, like the return Author[] from your functions. TypeScript will use type inference to figure it out by itself.
In your specific example either you leave the return type defined, and typescript will make the required checks or use something similar to what you have in getAuthors3. If you get or have the objects already typed just place them in the array and TypeScript will do the rest:
getAuthors() {
const author: Author = {
FirstName: 'John',
MI: 'J',
LastName: 'Smith',
};
return [author];
}
Return the array, not the result of an array assignment.
function getAuthors() {
return [{
FirstName: "John",
MI: "J",
LastName: "Smith"
}];
}
console.log(getAuthors());

How to destructure into dynamically named variables in ES6?

Let's suppose I have the following object:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
And that I want only the id and fullName.
I will do the following :
const { id, fullName } = user
Easy-peasy, right?
Now let's suppose that I want to do the destructuring based on the value of another variable called fields.
const fields = [ 'id', 'fullName' ]
Now my question is : How can I do destructuring based on an array of keys?
I shamelessly tried the following without success:
let {[{...fields}]} = user and let {[...fields]} = user. Is there any way that this could be done?
Thank you
It's not impossible to destructure with a dynamic key. To prevent the problem of creating dynamic variables (as Ginden mentioned) you need to provide aliases.
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
const {[fields[0]]: id, [fields[1]]: fullName} = user;
console.log(id); // 42
console.log(fullName); // { firstName: "John", lastName: "Doe" }
To get around the problem of having to define static aliases for dynamic values, you can assign to an object's dynamic properties. In this simple example, this is the same as reverting the whole destructuring, though :)
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName' ];
const object = {};
({[fields[0]]: object[fields[0]], [fields[1]]: object[fields[1]]} = user);
console.log(object.id); // 42
console.log(object.fullName); // { firstName: "John", lastName: "Doe" }
sources:
https://twitter.com/ydkjs/status/699845396084846592
https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch2.md#not-just-declarations
Paul Kögel's answer is great, but I wanted to give a simpler example for when you need only the value of a dynamic field but don't need to assign it to a dynamic key.
let obj = {x: 3, y: 6};
let dynamicField = 'x';
let {[dynamicField]: value} = obj;
console.log(value);
Short answer: it's impossible and it won't be possible.
Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.
Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.
As discussed before, you can't destruct into dynamically named variables in JavaScript without using eval.
But you can get a subset of the object dynamically, using reduce function as follows:
const destruct = (obj, ...keys) =>
keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const object = {
color: 'red',
size: 'big',
amount: 10,
};
const subset1 = destruct(object, 'color');
const subset2 = destruct(object, 'color', 'amount', 'size');
console.log(subset1);
console.log(subset2);
You can't destruct without knowing the name of the keys or using an alias for named variables
// you know the name of the keys
const { id, fullName } = user;
// use an alias for named variables
const { [fields[0]]: id, [fields[1]]: fullName } = user;
A solution is to use Array.reduce() to create an object with the dynamic keys like this:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
const fields = [ 'id', 'fullName', 'age' ];
const obj = fields.reduce((acc, k) => ({ ...acc, ...(user.hasOwnProperty(k) && { [k]: user[k] }) }), {});
for(let k in obj) {
console.log(k, obj[k]);
}
I believe that the above answers are intellectual and valid because all are given by pro developers. :). But, I have found a small and effective solution to destructure any objects dynamically. you can destructure them in two ways. But both ways are has done the same action.
Ex:
const user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe"
}
};
using "Object.key", "forEach" and "window" object.
Object.keys(user).forEach(l=>window[l]=user[l]);
Simply using Object. assign method.
Object.assign(window, user)
Output:
console.log(id, displayName, fullName)
// 42 jdoe {firstName: "John", lastName: "Doe"}
Anyway, I am a newbie in JS. So, don't take it as an offense if you found any misleading info in my answer.

Refactor R.filter and R.map to just R.reduce in pointfree style

I've started learning Ramda.js and functional programming and am very interested in functional composition using the pointfree style of programming, but I am having difficulty getting my head around some of it and I'm hoping someone can help illustrate:
Suppose I have a list of people - I want to only get the people between an inclusive age range of 13-19 (teenagers). Then, I want to map each person to either the return value of the person's getName() method (if it exists), else their name property. Then, I want to call .toUpperCase() on the names.
If I were using regular JS prototype methods, I wouldn't use Array.prototype.filter to get the teenage users and then Array.prototype.map. For performance reasons, I'd use Array.prototype.reduce, the body of which would be guarded by a conditional that checks if each iterated item meets the criteria for a teenager. This way, I'm iterating one less time. Elijah Manor has an article about this on his blog.
Here's the pointfree Ramda code I've come up with using R.filter and R.map (this works as expected):
var people = [
{ name: 'Bob', gender: 'male', age: 22 },
{ name: 'Jones', gender: 'male', age: 15 },
{ name: 'Alice', gender: 'female', age: 19 },
{ name: 'Carol', gender: 'female', age: 32 },
{ name: 'Odu', gender: 'male', age: 25 },
{ name: 'Fred', gender: 'male', age: 55 },
{ name: 'Nicole', gender: 'female', age: 29 },
{ getName: function() { return 'David' }, gender: 'male', age: 23 }
]
var getUpcasedTeenagerNames = R.pipe(
R.filter(
R.propSatisfies(R.both(R.lte(13), R.gte(19)), 'age')
),
R.map(
R.pipe(
R.ifElse(
R.propIs(Function, 'getName'),
R.invoker(0, 'getName'),
R.prop('name')
),
R.toUpper
)
)
)
getUpcasedTeenagerNames(people) // => ['JONES', 'ALICE']
My question is - how would I re-write the following native version of the above algorithm to use pointfree Ramda.js?
var getUpcasedTeenagerNames = function(people) {
return people
.reduce(function(teenagers, person) {
var age = person.age
if (age >= 13 && age <= 19) {
var name
if (typeof (name = person.getName) === 'function') {
name = name()
} else {
name = person.name
}
teenagers.push(name.toUpperCase())
}
return teenagers
}, [])
}
I've tried doing it with R.scan, looked at using R.reduced and R.when but I'm afraid I might be missing the point a little bit.
For your convenience, I've included this code on the Ramda REPL: http://goo.gl/6hBi5k
First of all, I would break the problem down a little differently. I would use Ramda's R.__ placeholder to fill in the first argument to R.lte and R.gte, so that they read better. I like to alias this with a plain underscore, so this will read R.both(R.gte(_, 13), R.lte(_, 19)), which I find more readable. Then I would separate out the function which finds the name on a person. That's clearly a stand-alone bit of code, and pulling that out makes the main code much more readable.
Finally, and here's the big thing, if you learn a bit about Transducers, you will learn a trick that stops the need to worry about the intermediate collections that are the possible performance problem in the initial technique.
var _ = R.__;
var findName = R.ifElse(
R.propIs(Function, 'getName'),
R.invoker(0, 'getName'),
R.prop('name')
);
var getUpcasedTeenagerNames = R.into([], R.compose(
R.filter(
R.propSatisfies(R.both(R.gte(_, 13), R.lte(_, 19)), 'age')
),
R.map(R.pipe(findName, R.toUpper))
));
getUpcasedTeenagerNames(people); //=> ["JONES", "ALICE"]
Now I wouldn't worry about the performance at all, unless I've found a performance problem and shown that this section of code is a major culprit. But if I had, then I could fix it by using transducers, and since both map and filter already work, all I need to do is to switch the direction of my composition (here by changing from pipe to compose) and wrap it up with into([]).
If you're interested, here's an article on user transducers in Ramda, and another good intro to transducers.
If I find a little time, I will see if I can convert your code into a points-free solution, but please don't make a fetish out of points-free. It's a useful
technique in some circumstance, but we shouldn't feel driven to use it where it doesn't easily fit.

Categories