Should I deep copy when modifying nested object? - javascript

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.

Related

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

JavaScript delete operator and Object.create() method

I am trying to remove a property from an Person object like this:
const Person = {
firstname: 'John',
lastname: 'Doe'
}
console.log(Person.firstname);
// Output: "John"
delete Person.firstname;
console.log(Person.firstname);
// Output: undefined
When I am using this delete operator is working fine and Person.firstname log is showing as undefined as expected. But when I create a new object using this Person object using Object.create() method like this:
const Person = {
firstname: 'John',
lastname: 'Doe'
}
const Person2 = Object.create(Person);
console.log(Person2.firstname);
// Output: "John"
delete Person2.firstname;
console.log(Person2.firstname);
// expected output: undefined
// actual output: "John"
You can see Person2.firstname is returning "John" in the end, when I was expecting it to work same way as done in the first snippet and return undefined.
So, my questions here are:
Why is delete Person2.firstname not working?
Also, how can we delete firstname property from the Person2 object?
Thanks for your help.
delete will only successfully remove a property from an object if the property to be deleted is an own non-configurable property. Here, your Person2 does not have an own property of firstname, so delete Person2.firstname doesn't work. The property exists on the internal prototype of Person2, but not on Person2 itself.
To delete the property, you'll have to call delete with the prototype object:
delete Person.firstname;
const Person = {
firstname: 'John',
lastname: 'Doe'
}
const Person2 = Object.create(Person);
delete Person.firstname;
console.log(Person2);
or, if you don't already have a reference to it, use Object.getPrototypeOf:
delete Object.getPrototypeOf(Person2).firstname;
const Person = {
firstname: 'John',
lastname: 'Doe'
}
const Person2 = Object.create(Person);
delete Object.getPrototypeOf(Person2).firstname;
console.log(Person2);
you are creating a shallow copy and you want to do deep copy
tht can be achieved by something like
const Person = {
firstname: 'John',
lastname: 'Doe'
}
const Person2 = Object.assign({},Person);
console.log(Person2.firstname);
// Output: "John"
delete Person2.firstname;
console.log(Person2.firstname);
// expected output: undefined
// actual output: "undefined"
console.log(Person2.lastname)
this will give you more clarity on shallow and deep copy

JavaScript - Exporting a harcoded array vs creating one

Let's say I have a file data.js, which contains an array of some data that will be imported somewhere (eg. a React component).
EXAMPLE A:
const DATA = [
{
firstName: 'jim',
lastName: 'beam',
fullName: 'jim beam'
},
{
firstName: 'jack',
lastName: 'daniels',
fullName: 'jack daniels'
}
];
export default DATA;
Ok, cool. Thing is, we're writing out the fullName property, which could be gathered by combining firstName and lastName. This is a very trivial example for clarity, so bear with me. We could also do something like this:
EXAMPLE B:
const DATA = [
{ firstName: 'jim', lastName: 'beam' },
{ firstName: 'jack', lastName: 'daniels' }
];
export default DATA.map(person => ({
...person,
fullName: `${person.firstName} ${person.lastName}`
});
Heck, we could even do this!
EXAMPLE C:
const DATA = ['jim beam', 'jack daniels'];
export default DATA.map(person => {
const [firstName, lastName] = person.split(' ');
return {
firstName,
lastName,
fullName: person
};
};
So, imagine you have a huuge list of data, where multiple values could be derived from one initial value. My question is how would examples B and C differ from just hardcoding everything right off the bat like example A?
If you had hundreds of items, examples B and C could have much less overhead, a smaller file size, and can reduce potential typos... But, we're declaring an array and then exporting a different one, which I assume could have a performance dip? Thoughts?
How about a class with a getter that evaluates fullName on access?
It offers smaller in-memory size, and doesn't have a performance issue because fullName property of each datum is not computed until they are imported, and accessed.
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
get fullName() {
return `${this.firstName} ${this.lastName}`
}
}
let a = new Person('Charles', 'Martel');
console.log(a.fullName)
// expected result: "Charles Martel"
Your data can then be declared as the following.
const DATA = [
new Person('jim', 'beam'),
new Person('jack','daniels')
];

How to populate an array in javascript if I want to have multiple elements for a single index of an array

How to populate an array in javascript when I want to store multiple elements in a single index?
I want to populate an array in Javascript. What I am trying to do is to store both firstName and lastName of a person in an array index. I am trying to do person[0].firstName = 'Dilshadur'; and then person[0].secondName = 'Rahman'; but It's not working and I think this is not the correct syntax to do so.
person[0].firstName.fill('Md Dilshadur Rahman');
person[0].lastName = 'Rahman';
person[1].firstName = 'Tabassum Monia';
person[1].lastName = 'Disha';
I am getting in the console something like this: "Uncaught TypeError: Cannot read property 'firstName'", I think this because of the wrong syntax and I am not being able to find the correct syntax.
You would have to have an object assigned to each index of the array, and each of those objects would have the firstName and lastName properties.
Ex:
let person = [];
person[0] = {firstName: 'Dilshadur', lastName: 'Rahman'};
person[1] = {firstName: 'Tabassum Monia', lastName: 'Disha'};
You should consider creating a separate class or something where this structure is defined.
class Person {
constructor(first, last) {
this.firstName = first;
this.lastName = last;
}
}
let person = [];
person[0] = new Person('Dilshadur', 'Rahman');
person[1] = new Person('Tabassum Monia', 'Disha');
You need to store an array of objects in that case.
const results = [
{ firstName: 'clark', lastName: 'kent' },
{ firstName: 'bruce', lastName: 'wayne' }
]
You can then use the spread operator to populate the entire index.
const newResults = [
{ firstName: 'peter', lastName: 'parker' },
...results
]
You can also push to your array, however it is good practice to not mutate your data and instead create an updated copy of it.
You can create array of objects
person = [];
person[0] = { firstName: "abc" , lastName: "def" } :
Or you can push values to it by
person.push( { firstName: "abc" , lastName: "def" } )

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