Javascript: setting object properties - javascript

I want to be able to do this:
var user1 = {
name: 'John',
gender: 'male'
}
var user2 = {
name: 'James',
gender: 'male',
email: 'james#gmail.com'
}
user1.someSetMethod({email: 'john#gmail.com'});
user2.someSetMethod({name: 'Jenny', gender: 'female'});
Desired Outcome:
var user1 = {
name: 'John',
gender: 'male',
email: 'john#gmail.com'
}
var user2 = {
name: 'Jenny',
gender: 'female',
email: 'james#gmail.com'
}
I want a method that will set attributes according to what is passed into the function. Is the attribute doesn't exist I want it to be created, if it does, I want it to be overwritten.
Does a method like this exist in Javascript?

This is normally called extending your object. Virtually every JavaScript library has its own method to do this. Or you can write your own in a few lines of code.
jQuery extend
underscore.js extend
ExtJS extend
MooTools extend
Prototype extend
Using jQuery's method, you'd do it like so:
var user1 = {
name: 'John',
gender: 'male'
};
$.extend(user1, {
email: 'john#gmail.com'
});
user1.email === 'john#gmail.com'; //true

No, You don't want to do this.
The only way to add a method to all objects is to extend Object.prototype.
Doing so (in ES3 browsers like IE8) has a consequence of adding properties to enumerations.
For example:
Object.prototype.extend = function (o) {
// clone properties of o into this
};
var someObj = {};
for (var key in someObj) {
console.log(key); // "extend"
}
The best you can do is use Object.extend
Object.extend(user2, {
name: "Jenny",
gender: "female"
});
pd has an implementation of extend or you can use:
Object.extend = function (target, source) {
for (var key in source) {
target[key] = source[key];
}
};

If you are willing to use jquery, you can use it's extend method, along with some defaults that will make sure all of the properties you want are set correctly.
Something like this:
function whatever(obj1) {
var defaults = {
name: 'Foo Bar',
email: 'email#example.com',
gender: 'male'
};
return $.extend(defaults, obj1);
}

Related

Should I deep copy when modifying nested object?

I recently started learning functional programming in Javascript, and one thing that's valued is Immutability. One way to preserve immutability is to when we want to modify some object that we first create a copy and then modify that copy and return it, for example:
const person = {
firstName: 'John',
lastName: 'Smith',
}
const modifyFName = obj => {
const objCopy = {...obj};
objCopy.firstName = 'James';
return objCopy;
}
console.log(modifyFName(person)) // {firstName: 'James', lastName: 'Smith'}
console.log(person) // {firstName: 'John', lastName: 'Smith'}
But if I want to modify some deeply nested object creating shallow copy like one above wouldn't make much of a difference, for example:
const person = {
firstName: 'John',
lastName: 'Smith',
parents: {
mom: {
firstName: 'Jennifer',
lastName: "Swift"
},
dad: {
firstName: 'Tom',
lastName: 'Cartman'
}
}
}
const modifyDadName = obj => {
const objCopy = {...obj};
objCopy.parents.dad.firstName = 'Aurelion';
return objCopy;
}
console.log(modifyDadName(person)) // {... dad: "Aurelion"}
console.log(person) // {... dad: "Aurelion"}
Both objects have changed. So I am wondering in this situation should I perhaps use deep copy, or maybe use some third-party Immutable data structures, or is there some other solution for this?
You're right that a shallow copy doesn't help, but you don't need a full deep copy, you only have to make copies of the things you want to change. In your case, that's person, parents, and dad, but not mom:
const modifyDadName = obj => {
return { // Replacement `obj` (`person`)
...obj,
parents: { // Replacement `obj.parents`
...obj.parents,
dad: { // Replacement `obj.parents.dad`
...obj.parents.dad,
firstName: "Aurelion", // New name
}
}
};
};
dad changes because, well, that's the operation we're doing (changing firstName). :-) parents changes because changing dad means there's a new object on its dad property. person (obj) changes because there's a new parents object. But mom doesn't change, so we can reuse the same object.

Refactoring a destructuring into an object destructuring

I am refactoring my code, which involves converting a big list of let statements into an object called personDetails:
personDetails = {
firstName: '',
lastName: '',
zipcode: 'xyz',
age: 20,
gender: 'm'
}
Currently, I am destructuring the values returned from my array like this:
[firstName, lastName] = getNames(zipcode, age, gender)
This works fine. But now that I am switching to an object, how do I update that object with the returned values? I will be passing in the object as an argument like this:
getNames(personDetails)
Do I have to do something like this?
personDetails = getNames(personDetails)
The called function might look something like this (abbreviated):
const getNames(personDetails) => {
personDetails.firstname = 'Jack'
personDetails.lastName = 'Jones'
}
1) Your arrow function had a typo, you must declare it with an = before the argument, like this:
const getNames = (personDetails) => { // Correct
const getNames(personDetails) => { // Incorrect
2) Inside your function, you weren't modifying an object key, but creating a new one instead. Remember that objects keys differs if you use upper or lowercase letters, firstName and firstname are not the same key.
3) Last, when you create an argument in your function, do not declare it with the same name of the global object, since it could create unexpected results. Then, you don´t need to destructure your object, just return the complete object.
let personDetails = { // Using let
firstName: '',
lastName: '',
zipcode: 'xyz',
age: 20,
gender: 'm'
};
const getNames = (obj) => { // obj is the argument
obj.firstName = 'Jack';
obj.lastName = 'Jones';
return obj; // Return complete object
}
personDetails = getNames(personDetails);
console.log(personDetails);
If you want to destructure the object, you can do it too the same way you do it with the array, but I wouldn´t recommend it because it makes the code less clear:
const personDetails = { // Using const
firstName: '',
lastName: '',
zipcode: 'xyz',
age: 20,
gender: 'm'
};
const getNames = (obj) => { // obj is the argument
obj.firstName = 'Jack';
obj.lastName = 'Jones';
return [obj.firstName, obj.lastName]; // Return part of object as an array
}
[personDetails.firstName, personDetails.lastName] = getNames(personDetails);
console.log(personDetails);

Object Destructuring ignoring values

I'm trying to destructure and ignore few values, how do I do that?
For example:
const test = { name: 'John', age: 29, gender: 'male'}
function getData(...args) {
const {,,gender} = args[0];
console.log(gender); // should print male.
}
getData(test);
I want to ignore (not declare variables for) name and age parameters (so that my ESLint does not throw an error) at the same time use ES6.
The syntax , does not seem to work either. Any other workarounds for this problem?
You have a single arg (the object), and you should object destructuring to get gender:
const test = { name: 'John', age: 29, gender: 'male'}
function getData(arg) {
const { gender} = arg;
console.log(gender); // should print male.
}
getData(test);

Setting a default value for object properties

Is it possible to multiple values for one property in objects? Here's what I mean:
// Note: This will give you an error if you try this. It's just an example.
var person = {
name: "John",
eyeColor: "blue" | "green"
};
How it should works:
So for eyeColor, the default is blue, but if it's set to green, then it should ignore the blue.
The code above is obviously not executable, but is there something similar I can do to achieve that?
You can achieve this with a bit of reflection.
I've created a rough implementation of a function which takes an object, the name of a property and a default value. It creates get and set methods for the property in question.
function addDefaultProperty(object, propName, defaultValue) {
var propValue;
Object.defineProperty(object, propName, {
// this method is called when the property is set
// e.g. object.propName = value
set: function(value) {
propValue = value;
},
// this method is called when the property is accessed
// e.g. console.log(object.propName)
get: function() {
var hasValue = typeof propValue != 'undefined';
return hasValue ? propValue : defaultValue;
}
});
return object;
}
Then you can simply create your object however you want and use that method to create properties with default values.
var person = {
name: "John",
};
addDefaultProperty(person, 'eyeColor', 'blue');
console.log(person.eyeColor); // "blue"
person.eyeColor = "green";
console.log(person.eyeColor); // "green"
person.eyeColor = undefined;
console.log(person.eyeColor); // "blue"
Here's a very small example of what you're trying to accomplish, and in the same style you're trying to accomplish it... If you don't pass an arg for eyeclr, it will default to 'blue' here.
function newPerson(nm, eyeclr) {
return {
name: nm,
eyeColor: eyeclr || "blue"
};
}
> newPerson('john')
Object {name: "john", eyeColor: "blue"}
> newPerson('john', 'green')
Object {name: "john", eyeColor: "green"}
Is it possible to multiple values for one property in objects?
Yes, something like this will do:
var person = {
name: "John",
eyeColor: ["blue","green"]
};
That answers your first question.
NOTE: I think your logic is a bit incoherent IMHO.
For your second requirement, I think you're better off writing a setter function:
var person = {
name: "John",
eyeColor: "blue",
setEyeColor:function(color){ this.eyeColor=color;}
};
alert(person.eyeColor);
person.setEyeColor('purple');
alert(person.eyeColor);

What is the data type and how to initialize it?

demos.ListStore = new Ext.data.Store({
model: 'Contact',
sorters: 'firstName',
getGroupString : function(record) {
return record.get('firstName')[0];
},
data: [
{firstName: 'Julio', lastName: 'Benesh'},
{firstName: 'Julio', lastName: 'Minich'},
{firstName: 'Tania', lastName: 'Ricco'},
{firstName: 'Odessa', lastName: 'Steuck'},
{firstName: 'Nelson', lastName: 'Raber'},
{firstName: 'Tyrone', lastName: 'Scannell'},
{firstName: 'Allan', lastName: 'Disbrow'},
{firstName: 'Cody', lastName: 'Herrell'},
{firstName: 'Julio', lastName: 'Burgoyne'},
{firstName: 'Jessie', lastName: 'Boedeker'},
{firstName: 'Allan', lastName: 'Leyendecker'},
{firstName: 'Javier', lastName: 'Lockley'},
{firstName: 'Guy', lastName: 'Reasor'},
...,
...
]
})
In my understanding, "data" is not dictionary or array type of javascript.
Is this kind of data type additionally supported by jQuery?
My questions are:
What is the type of parameter of Ext.data.Store(...)?
Also, the type of data?
In this case, how can I dynamically init the data?
(I have a lot of data, so static assignment is not desirable.)
Thanks a lot!
1) What is the type of parameter of Ext.data.Store(...)?
It's just a plain object with a bunch of properties being used as an associative array. Pretty much all function arguments in Ext JS are passed in using a single object like this. In Ext JS terms, they call this a "configuration object".
2) Also, the type of data?
Data is a array. Each element of the array is an object with a bunch of properties used as an associative array, similar to the parameter of Ext.data.Store().
3) In this case, how can I dynamically init the data?
You can declare an empty array to start and add it to your configuration object. Then declare each row separately and push them into that array, finally use the configuration object when you call new Ext.data.Store().
Hopefully these code samples will be self-explanatory.
var cfg = new Object(); // same as var cfg = {};
cfg.model = "Contact"; // one way to set a property
cfg["sorters"] = "firstName"; // another way to set a property
// one way to set the function
cfg.getGroupString = function(record) {
return record.get('firstName')[0];
};
// here's another way to set the function
function myGetGroupString(record) {
return record.get('firstName')[0];
}
cfg.getGroupString = myGetGroupString;
// initializing data long-hand
cfg.data = new Array(); // same as cfg.data = [];
var record1 = new Object();
record1.firstName = "Mike";
record1.lastName = "Lin";
cfg.data.push(record1);
// another way to define a record
var record2 = {firstName: "Jason", lastName: "Mraz"};
cfg.data.push(record2);
// cfg is just a plain old object used as an associative array/hash
demos.ListStore = new Ext.data.Store(cfg);
Why can't you initialize it as a empty object and then fill it afterwards with your content?
Anyway:
http://dev.sencha.com/deploy/dev/docs/source/Store.html#cls-Ext.data.Store
The parameter passed to Ext.data.Store() is an object, a.k.a. a dictionary or associative array in other languages. The data property of this object (data : [{…},…] is a simple array filled with more objects.
The function is taking one parameter and is a javascript Object type.
{
model: 'Contact',
sorters: 'firstName',
getGroupString : function(record) {
return record.get('firstName')[0];
},
data: [ ... ] ;
}
This can directly be associated to a variable and then passed as parameter too.
var param1 = {
model: 'Contact',
sorters: 'firstName',
getGroupString : function(record) {
return record.get('firstName')[0];
},
data: [ ... ]
}
The data is a variable in param1 object which contains array of javascript objects.

Categories

Resources