How to use an object as a function parameter in JS? - javascript

I created a function that should return an object with user data. I want the function to accept an object as a parameter and I'd like this object to be defined inside the function with all the values pre-defined as default parameters in case they are not passed at a function call. The example code has only 2 values but I will need to pass over 15. How can I achieve such a solution?
const userCreator = (name, age) => {
if (name === undefined) {
name = 'John'
}
if (age === undefined) {
age = 25
}
return {
name:name,
age:age
}
};

...and I'd like this object to be defined inside the function with all the values pre-defined as default parameters in case they are not passed at a function call.
I think you're saying:
You want the function to accept an object
You want all the properties of that object to be optional with defaults specified by the function
To do that, you can use destructuring defaults. But see also below, because in this specific case, where you want to return an object, you may want to use a different approach.
But let's start with just basic destructuring without defaults, then add them:
const userCreator = ({ name, age }) => {
// ^−−−−−−−−−−−^−−−−−−−−−−−−−−−− destructuring
// ...stuff with `name` and `age` here...
return {
name,
age,
};
};
To add defaults for those properties, you add them within the destructuring pattern (the {} where the parameter name would otherwise be):
const userCreator = ({ name = "John", age = 25, }) => {
// ^^^^^^^^^−−−−−^^^^^−−−− defaults for the properties
return {
name,
age,
};
};
If there are lots, probably best to break up into lines:
const userCreator = ({
name = "John",
age = 25,
}) => {
return {
name,
age,
};
};
That version still expects an object to be provided, though. If you want to allow userCreator() (no parameter passed at all), you need to add a default parameter value for the object parameter:
const userCreator = ({
name = "John",
age = 25,
} = {}) => {
//^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− default for the parameter
return {
name,
age,
};
};
That uses {} as the default value if no parameter is provided at all, "John" as the default if name isn't provided, and 25 as the default if age isn't provided. And since there is no name or age on the default {}, they get defaulted when you do userCreator().
The alternative approach:
Since you want to return an object, you might just accept the object parameter directly, then use property spread or Object.assign to fill in defaults, like this:
const userDefaults = {
name: "John",
age: 25,
};
const userCreator = (template) => {
const result = { // The result object we'll return
...userDefaults, // Fill in defaults
...template // Overwrite with any properties from the caller
};
// Or with `Object.assign` in older environments without property spread:
//const result = Object.assign(
// {}, // The result object we'll return
// userDefaults, // Fill in defaults
// template // Overwrite with any properties from the caller
//);
return result;
};
Property spread and Object.assign both ignore null or undefined, so if no object is passed at all, template is undefined, and

You should define default values directly in the function parameters:
const userCreator = (name = 'John', age = 25) => {
return {
name: name,
age: age
}
};

Related

Check object variable exists when using object destructuring

I have a 'getData' function which gets passed a 'data' variable. Inside the function I'm trying to access an object 'data.someObject' which may or may not exist because it comes from an outside library.
What is the best approach to check if it does exist before trying to use it? Also see I'm using object destructuring here aswell.
const getData = (data) => {
const { name, age } = data.someObject; // someObject may or may not exist
console.log(name, age);
}
I thought doing this might work:
const { name, age } = data.someObject || {};
But I wasn't sure if that would throw an error.
Thanks
You can use || & if required provide default values while destructuring.
function print(user) {
const { fname = "Anonymous", lname = "Panda" } = user.fullname || {};
console.log(`Hello ${fname} ${lname}`);
}
print({ fullname: { fname: "John", lname: "Doe" } });
print({});
print({ fullname: null });
Personally I would put an if statement:
const getData = (data) => {
if (data.someObject == null) {
// Here you can return, throw an error or do anything else you might need
}
const { name, age } = data.someObject; // someObject may or may not exist
console.log(name, age);
}

How to destructure and reassign in one line using JavaScript [duplicate]

This question already has answers here:
One-liner to take some properties from object in ES 6
(12 answers)
Closed 1 year ago.
I have an object adData and I need to extract some of it's properties, add some more properties to the extracted object and pass the object as parameter. I can do this using:
const params = {};
params.id = adData.id;
params.status = adData.status;
params.frequency = adData.frequency;
params.user = getLoggedInUser();
callAnotherFunction(params)
Can I do the destructing and reassigning to new object in one line ? Something like:
const params = {id, status, frequency} = adData;
params.user = getLoggedInUser();
Or
const params = {id, status, frequency, getLoggedInUser(): user} = adData;
Now these both above syntaxes are wrong but is there any other way to do it using destructuring and without extracting the properties one by one
If you know what properties the object does have, and there aren't that many, you can list them and use rest syntax to gather the others into an object:
const { unwantedProp, ...params) = adData;
// use params
Otherwise, there isn't any incredibly simple syntax for what you want, though you could
const params = Object.fromEntries(
Object.entries(adData).filter(([key]) =>
['id', 'status', 'frequency'].includes(key)
)
);
We can do in one line with destructuring and arrow function.
const getLoggedInUser = () => "foo";
const adData = {
id: 123,
status: "active",
frequency: "less",
bar: 4,
};
const params = (({ id, status, frequency }, user = getLoggedInUser()) => ({
id,
status,
frequency,
user,
}))(adData);
console.log({ params });

Assign dynamically nested array of classes

I need to be able to receive data from an external API and map it dynamically to classes. When the data is plain object, a simple Object.assign do the job, but when there's nested objects you need to call Object.assign to all nested objects.
The approach which I used was to create a recursive function, but I stumble in this case where there's a nested array of objects.
Classes
class Organization {
id = 'org1';
admin = new User();
users: User[] = [];
}
class User {
id = 'user1';
name = 'name';
account = new Account();
getFullName() {
return `${this.name} surname`;
}
}
class Account {
id = 'account1';
money = 10;
calculate() {
return 10 * 2;
}
}
Function to initialize a class
function create(instance: object, data: any) {
for (const [key, value] of Object.entries(instance)) {
if (Array.isArray(value)) {
for (const element of data[key]) {
// get the type of the element in array dynamically
const newElement = new User();
create(newElement, element)
value.push(newElement);
}
} else if (typeof value === 'object') {
create(value, data[key]);
}
Object.assign(value, data);
}
}
const orgWithError = Object.assign(new Organization(), { admin: { id: 'admin-external' }});
console.log(orgWithError.admin.getFullName()); // orgWithError.admin.getFullName is not a function
const org = new Organization();
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
create(org, data);
// this case works because I manually initialize the user in the create function
// but I need this function to be generic to any class
console.log(org.users[0].getFullName()); // "name surname"
Initially I was trying to first scan the classes and map it and then do the assign, but the problem with the array of object would happen anyway I think.
As far as I understand from your code, what you basically want to do is, given an object, determine, what class it is supposed to represent: Organization, Account or User.
So you need a way to distinguish between different kinds of objects in some way. One option may be to add a type field to the API response, but this will only work if you have access to the API code, which you apparently don't. Another option would be to check if an object has some fields that are unique to the class it represents, like admin for Organization or account for User. But it seems like your API response doesn't always contain all the fields that the class does, so this might also not work.
So why do you need this distinction in the first place? It seems like the only kind of array that your API may send is array of users, so you could just stick to what you have now, anyway there are no other arrays that may show up.
Also a solution that I find more logical is not to depend on Object.assign to just assign all properties somehow by itself, but to do it manually, maybe create a factory function, like I did in the code below. That approach gives you more control, also you can perform some validation in these factory methods, in case you will need it
class Organization {
id = 'org1';
admin = new User();
users: User[] = [];
static fromApiResponse(data: any) {
const org = new Organization()
if(data.id) org.id = data.id
if(data.admin) org.admin = User.fromApiResponse(data.admin)
if(data.users) {
this.users = org.users.map(user => User.fromApiResponse(user))
}
return org
}
}
class User {
id = 'user1';
name = 'name';
account = new Account();
getFullName() {
return `${this.name} surname`;
}
static fromApiResponse(data: any) {
const user = new User()
if(data.id) user.id = data.id
if(data.name) user.name = data.name
if(data.account)
user.account = Account.fromApiResponse(data.account)
return user
}
}
class Account {
id = 'account1';
money = 10;
calculate() {
return 10 * 2;
}
static fromApiResponse(data: any) {
const acc = new Account()
if(data.id) acc.id = data.id
if(data.money) acc.money = data.money
return acc
}
}
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
const organization = Organization.fromApiResponse(data)
I can't conceive of a way to do this generically without any configuration. But I can come up with a way to do this using a configuration object that looks like this:
{
org: { _ctor: Organization, admin: 'usr', users: '[usr]' },
usr: { _ctor: User, account: 'acct' },
acct: { _ctor: Account }
}
and a pointer to the root node, 'org'.
The keys of this object are simple handles for your type/subtypes. Each one is mapped to an object that has a _ctor property pointing to a constructor function, and a collection of other properties that are the names of members of your object and matching properties of your input. Those then are references to other handles. For an array, the handle is [surrounded by square brackets].
Here's an implementation of this idea:
const create = (root, config) => (data, {_ctor, ...keys} = config [root]) =>
Object.assign (new _ctor (), Object .fromEntries (Object .entries (data) .map (
([k, v]) =>
k in keys
? [k, /^\[.*\]$/ .test (keys [k])
? v .map (o => create (keys [k] .slice (1, -1), config) (o))
: create (keys [k], config) (v)
]
: [k, v]
)))
class Organization {
constructor () { this.id = 'org1'; this.admin = new User(); this.users = [] }
}
class User {
constructor () { this.id = 'user1'; this.name = 'name'; this.account = new Account() }
getFullName () { return `${this.name} surname`}
}
class Account {
constructor () { this.id = 'account1'; this.money = 10 }
calculate () { return 10 * 2 }
}
const createOrganization = create ('org', {
org: { _ctor: Organization, admin: 'usr', users: '[usr]' },
usr: { _ctor: User, account: 'acct' },
acct: { _ctor: Account }
})
const orgWithoutError = createOrganization ({ admin: { id: 'admin-external' }});
console .log (orgWithoutError .admin .getFullName ()) // has the right properties
const data = { id: 'org2', admin: { id: 'admin2' }, users: [ { id: 'user-inside' }]}
const org = createOrganization (data)
console .log (org .users [0] .getFullName ()) // has the right properties
console .log ([
org .constructor .name,
org .admin .constructor.name, // has the correct hierarchy
org .users [0]. account. constructor .name
] .join (', '))
console .log (org) // entire object is correct
.as-console-wrapper {min-height: 100% !important; top: 0}
The main function, create, receives the name of the root node and such a configuration object. It returns a function which takes a plain JS object and hydrates it into your Object structure. Note that it doesn't require you to pre-construct the objects as does your attempt. All the calling of constructors is done internally to the function.
I'm not much of a Typescript user, and I don't have a clue about how to type such a function, or whether TS is even capable of doing so. (I think there's a reasonable chance that it is not.)
There are many ways that this might be expanded, if needed. We might want to allow for property names that vary between your input structure and the object member name, or we might want to allow other collection types besides arrays. If so, we probably would need a somewhat more sophisticated configuration structure, perhaps something like this:
{
org: { _ctor: Organization, admin: {type: 'usr'}, users: {type: Array, itemType: 'usr'} },
usr: { _ctor: User, account: {type: 'acct', renameTo: 'clientAcct'} },
acct: { _ctor: Account }
}
But that's for another day.
It's not clear whether this approach even comes close to meeting your needs, but it was an interesting problem to consider.

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.

Remove value from object without mutation

What's a good and short way to remove a value from an object at a specific key without mutating the original object?
I'd like to do something like:
let o = {firstname: 'Jane', lastname: 'Doe'};
let o2 = doSomething(o, 'lastname');
console.log(o.lastname); // 'Doe'
console.log(o2.lastname); // undefined
I know there are a lot of immutability libraries for such tasks, but I'd like to get away without a library. But to do this, a requirement would be to have an easy and short way that can be used throughout the code, without abstracting the method away as a utility function.
E.g. for adding a value I do the following:
let o2 = {...o1, age: 31};
This is quite short, easy to remember and doesn't need a utility function.
Is there something like this for removing a value? ES6 is very welcome.
Thank you very much!
Update:
You could remove a property from an object with a tricky Destructuring assignment:
const doSomething = (obj, prop) => {
let {[prop]: omit, ...res} = obj
return res
}
Though, if property name you want to remove is static, then you could remove it with a simple one-liner:
let {lastname, ...o2} = o
The easiest way is simply to Or you could clone your object before mutating it:
const doSomething = (obj, prop) => {
let res = Object.assign({}, obj)
delete res[prop]
return res
}
Alternatively you could use omit function from lodash utility library:
let o2 = _.omit(o, 'lastname')
It's available as a part of lodash package, or as a standalone lodash.omit package.
With ES7 object destructuring:
const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }
one line solution
const removeKey = (key, {[key]: _, ...rest}) => rest;
Explanations:
This is a generic arrow function to remove a specific key. The first argument is the name of the key to remove, the second is the object from where you want to remove the key. Note that by restructuring it, we generate the curated result, then return it.
Example:
let example = {
first:"frefrze",
second:"gergerge",
third: "gfgfg"
}
console.log(removeKey('third', example))
/*
Object {
first: "frefrze",
second: "gergerge"
}
*/
To add some spice bringing in Performance. Check this thread bellow
https://github.com/googleapis/google-api-nodejs-client/issues/375
The use of the delete operator has performance negative effects for
the V8 hidden classes pattern. In general it's recommended do not use
it.
Alternatively, to remove object own enumerable properties, we could
create a new object copy without those properties (example using
lodash):
_.omit(o, 'prop', 'prop2')
Or even define the property value to null or undefined (which is
implicitly ignored when serializing to JSON):
o.prop = undefined
You can use too the destructing way
const {remov1, remov2, ...new} = old;
old = new;
And a more practical exmple:
this._volumes[this._minCandle] = undefined;
{
const {[this._minCandle]: remove, ...rest} = this._volumes;
this._volumes = rest;
}
As you can see you can use [somePropsVarForDynamicName]: scopeVarName syntax for dynamic names. And you can put all in brackets (new block) so the rest will be garbage collected after it.
Here a test:
exec:
Or we can go with some function like
function deleteProps(obj, props) {
if (!Array.isArray(props)) props = [props];
return Object.keys(obj).reduce((newObj, prop) => {
if (!props.includes(prop)) {
newObj[prop] = obj[prop];
}
return newObj;
}, {});
}
for typescript
function deleteProps(obj: Object, props: string[]) {
if (!Array.isArray(props)) props = [props];
return Object.keys(obj).reduce((newObj, prop) => {
if (!props.includes(prop)) {
newObj[prop] = obj[prop];
}
return newObj;
}, {});
}
Usage:
let a = {propH: 'hi', propB: 'bye', propO: 'ok'};
a = deleteProps(a, 'propB');
// or
a = deleteProps(a, ['propB', 'propO']);
This way a new object is created. And the fast property of the object is kept. Which can be important or matter. If the mapping and the object will be accessed many many times.
Also associating undefined can be a good way to go with. When you can afford it. And for the keys you can too check the value. For instance to get all the active keys you do something like:
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k] !== undefined);
//or
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k]); // if any false evaluated value is to be stripped.
Undefined is not suited though for big list. Or development over time with many props to come in. As the memory usage will keep growing and will never get cleaned. So it depend on the usage. And just creating a new object seem to be the good way.
Then the Premature optimization is the root of all evil will kick in. So you need to be aware of the trade off. And what is needed and what's not.
Note about _.omit() from lodash
It's removed from version 5. You can't find it in the repo. And here an issue that talk about it.
https://github.com/lodash/lodash/issues/2930
v8
You can check this which is a good reading https://v8.dev/blog/fast-properties
As suggested in the comments above if you want to extend this to remove more than one item from your object I like to use filter. and reduce
eg
const o = {
"firstname": "Jane",
"lastname": "Doe",
"middlename": "Kate",
"age": 23,
"_id": "599ad9f8ebe5183011f70835",
"index": 0,
"guid": "1dbb6a4e-f82d-4e32-bb4c-15ed783c70ca",
"isActive": true,
"balance": "$1,510.89",
"picture": "http://placehold.it/32x32",
"eyeColor": "green",
"registered": "2014-08-17T09:21:18 -10:00",
"tags": [
"consequat",
"ut",
"qui",
"nulla",
"do",
"sunt",
"anim"
]
};
const removeItems = ['balance', 'picture', 'tags']
console.log(formatObj(o, removeItems))
function formatObj(obj, removeItems) {
return {
...Object.keys(obj)
.filter(item => !isInArray(item, removeItems))
.reduce((newObj, item) => {
return {
...newObj, [item]: obj[item]
}
}, {})
}
}
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
My issue with the accepted answer, from an ESLint rule standard, if you try to destructure:
const { notNeeded, alsoNotNeeded, ...rest } = { ...ogObject };
the 2 new variables, notNeeded and alsoNotNeeded may throw a warning or error depending on your setup since they are now unused. So why create new vars if unused?
I think you need to use the delete function truly.
export function deleteKeyFromObject(obj, key) {
return Object.fromEntries(Object.entries(obj).filter(el => el[0] !== key))
}
with lodash cloneDeep and delete
(note: lodash clone can be used instead for shallow objects)
const obj = {a: 1, b: 2, c: 3}
const unwantedKey = 'a'
const _ = require('lodash')
const objCopy = _.cloneDeep(obj)
delete objCopy[unwantedKey]
// objCopy = {b: 2, c: 3}
For my code I wanted a short version for the return value of map() but the multiline/mutli operations solutions were "ugly". The key feature is the old void(0) which resolve to undefined.
let o2 = {...o, age: 31, lastname: void(0)};
The property stays in the object:
console.log(o2) // {firstname: "Jane", lastname: undefined, age: 31}
but the transmit framework kills it for me (b.c. stringify):
console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
I wrote big function about issue for me. The function clear all values of props (not itself, only value), arrays etc. as multidimensional.
NOTE: The function clear elements in arrays and arrays become an empty array. Maybe this case can be added to function as optional.
https://gist.github.com/semihkeskindev/d979b169e4ee157503a76b06573ae868
function clearAllValues(data, byTypeOf = false) {
let clearValuesTypeOf = {
boolean: false,
number: 0,
string: '',
}
// clears array if data is array
if (Array.isArray(data)) {
data = [];
} else if (typeof data === 'object' && data !== null) {
// loops object if data is object
Object.keys(data).forEach((key, index) => {
// clears array if property value is array
if (Array.isArray(data[key])) {
data[key] = [];
} else if (typeof data[key] === 'object' && data !== null) {
data[key] = this.clearAllValues(data[key], byTypeOf);
} else {
// clears value by typeof value if second parameter is true
if (byTypeOf) {
data[key] = clearValuesTypeOf[typeof data[key]];
} else {
// value changes as null if second parameter is false
data[key] = null;
}
}
});
} else {
if (byTypeOf) {
data = clearValuesTypeOf[typeof data];
} else {
data = null;
}
}
return data;
}
Here is an example that clear all values without delete props
let object = {
name: 'Semih',
lastname: 'Keskin',
brothers: [
{
name: 'Melih Kayra',
age: 9,
}
],
sisters: [],
hobbies: {
cycling: true,
listeningMusic: true,
running: false,
}
}
console.log(object);
// output before changed: {"name":"Semih","lastname":"Keskin","brothers":[{"name":"Melih Kayra","age":9}],"sisters":[],"hobbies":{"cycling":true,"listeningMusic":true,"running":false}}
let clearObject = clearAllValues(object);
console.log(clearObject);
// output after changed: {"name":null,"lastname":null,"brothers":[],"sisters":[],"hobbies":{"cycling":null,"listeningMusic":null,"running":null}}
let clearObject2 = clearAllValues(object);
console.log(clearObject2);
// output after changed by typeof: {"name":"","lastname":"","brothers":[],"sisters":[],"hobbies":{"cycling":false,"listeningMusic":false,"running":false}}

Categories