I have this code :
function logInfos(user = {}) {
const redactedUser = {
firstName: "<REDACTED>",
lastName: "<REDACTED>",
address: {
city: "<REDACTED>",
country: "<REDACTED>",
},
};
const {
firstName,
lastName,
address: { city, country },
} = user;
console.log("partie user", firstName);
console.log("partie user", lastName);
const newUser = {
...user,
address: {
...user.address,
},
};
console.log("partie newuser", newUser);
console.log(`${newUser.firstName} ${newUser.lastName} lives in ${newUser.address.city}, ${newUser.address.country}.`);
}
How can I replace the value undefined of user object passed as argument and use the default value of redactedUser object instead?
function compareObj(defaultObj, targetObj) {
for(let key in defaultObj) {
if(!Array.isArray(defaultObj[key]) && defaultObj[key] !== null && typeof defaultObj[key] === "object") {
targetObj[key] = {};
copyObj(defaultObj[key], targetObj[key]);
} else {
if(!targetObj[key]) targetObj[key] = defaultObj[key];
}
}
}
compareObj(redactedUser, user);
You can place this code function inside logInfos.
What it does is, it iterates through all the properties of the default object and checks if the property exists in the target object.
In case the property does not exist in the target object, same property will be created in the target object and the value will be copied.
Related
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 10 months ago.
I am trying to get output from an function, where on calling it it should return John temp (2021) but am getting John undefined (undefined)
const user = {
username : 'John',
type: 'temp',
yearJoin: 2021,
userString(){
function getUserDetails (){
return `${this.type} (${this.yearJoin})`
}
return `${this.username} ${getUserDetails()}`;
}
}
console.log(user.userString())
You need to set the scope of the getUserDetails call to this:
getUserDetails.call(this)
See:
const user = {
username: 'John',
type: 'temp',
yearJoin: 2021,
userString() {
function getUserDetails() {
return `${this.type} (${this.yearJoin})`
}
return `${this.username} ${getUserDetails.call(this)}`;
}
}
console.log(user.userString());
You can clean this up, by moving the getUserDetails function up as a method on the object:
const user = {
username: 'John',
type: 'temp',
yearJoin: 2021,
getUserDetails() {
return `${this.type} (${this.yearJoin})`
},
toString() {
return `${this.username} ${this.getUserDetails()}`;
}
}
console.log(user.toString());
Now take it one step further, as a class, and you have:
class User {
constructor({ username, type, yearJoin }) {
this.username = username;
this.type = type;
this.yearJoin = yearJoin;
}
getUserDetails() {
return `${this.type} (${this.yearJoin})`
}
toString() {
return `${this.username} ${this.getUserDetails()}`;
}
}
const user = new User({
username: 'John',
type: 'temp',
yearJoin: 2021
});
console.log(user.toString());
everything works fine until it gets to the 2nd occurrence of contact.
TypeError: Cannot set property 'name' of undefined
Because the contact default in constructor has only 1 occurrence. Any way to work around it?
class Cust {
constructor(custData) {
this.address = {
countryCode: null,
address1: null,
address2: null,
city: null,
countrySubDivision: null,
postalCode: null
},
this.groupId = null;
this.contact = [{ name: null, phone: null }];
//this.contact.phone = custData.contact.phone
this.state = this._getState(custData.status || null);
this._setData(custData);
}
_getState(status) {
let state = (status == 'active' ? 'good' : 'bad');
return state;
}
_setData(data, prefix, index) {
let result;
for (let key in data) {
let value = data[key];
let valueIsNullOrEmpty = !value;
if (!valueIsNullOrEmpty && typeof value === 'object') {
if (Array.isArray(value)) {
value = value
.map((subProperty, index) => this._setData(subProperty, key, index))
.filter((subProperty) => Object.keys(subProperty).length > 0);
valueIsNullOrEmpty = value.length === 0;
continue;
} else {
value = this._setData(value, key);
valueIsNullOrEmpty = Object.keys(value).length === 0;
continue;
}
}
if (prefix) {
if (index >= 0) {
this[prefix][index][key] = data[key];
}
else {
this[prefix][key] = data[key];
}
}
else {
this[key] = data[key]
}
result = data[key];
}
console.log(JSON.stringify(this));
return result;
}
}
var custData = {
id: 1,
name: "Barr",
// groupId: 2,
status: "active",
address: {
countryCode: "USA",
address1: "123 main street",
address2: null,
city: "Chicago",
postalCode: "85001"
}, contact: [
{
phone: "222-222-2222"
},
{
name: "Tim"
}]
}
var cust = new Cust(custData);
You are recursively formatting the data, but you always try to change the mutated data from this, e.g.
this[key]
That will work for depth 1, but for a depth of let's say 5 it gets complicated:
this[key1][key2][key3][key4][key5]
you get the point (and thats where your code actually fails, accessing a property of a nested object with a depth greater than 2).
this will never work. Instead pass the object to modify into the method (which can be a function then), you could also keep it immutable then by returning a new object (that will make debugging easier):
function format(obj) {
const result = {};
//...
return result;
}
Then you can easily call format with nested objects.
From inside the class that can be called as:
Object.assign(this, format(data));
I am having trouble trying to append something my object, using the spread syntax.
Depending on the fact whether the NewPerson is there for a private/professional occasion I want to append additional key/values to the object/array.
Somehow it does not work. Hopefully someone can help me out. :(
var NewPerson = [
Firstname: this.state.addPersonFirstname,
Lastname: this.state.addPersonLastname,
Birthday: this.state.addPersonBirthday,
Occasion: this.state.addPersonOccasion,
];
if (this.state.addPersonOccasion === 'OccasionProfessional') {
NewPerson = [
...NewPerson,
...[ProfEmployerName: this.state.addPersonOccasionProfEmployerName],
...[ProfEmployerPLZ: this.state.addPersonOccasionProfEmployerPLZ],
...[ProfEmployerCity: this.state.addPersonOccasionProfEmployerCity],
...[ProfEmployerUVT: this.state.addPersonOccasionProfEmployerUVT]
]
}
if (this.state.addPersonOccasion === 'OccasionPrivate') {
NewPerson = [
...NewPerson,
...[PrivPersonStreet: this.state.addPersonOccasionPrivPersonStreet],
...[PrivPersonPLZ: this.state.addPersonOccasionPrivPersonPLZ],
...[PrivPersonCity: this.state.addPersonOccasionPrivPersonCity]
]
}
var CombinedPersons
if (PreviousPersons === null) {
CombinedPersons = NewPerson
} else {
CombinedPersons = [...PreviousPersons, ...NewPerson]
}
You should use Objects instead Array because Objects have key-value pairs. You could do (in ES6 syntax):
const { addPersonOccasion } = this.state;
const isProfessional = addPersonOccasion === 'OccasionProfessional';
const isPrivate = addPersonOccasion === 'OccasionPrivate';
const NewPerson = {
Firstname: this.state.addPersonFirstname,
Lastname: this.state.addPersonLastname,
Birthday: this.state.addPersonBirthday,
Occasion: this.state.addPersonOccasion,
...(isProfessional && {
ProfEmployerName: this.state.addPersonOccasionProfEmployerName,
ProfEmployerPLZ: this.state.addPersonOccasionProfEmployerPLZ,
ProfEmployerCity: this.state.addPersonOccasionProfEmployerCity,
ProfEmployerUVT: this.state.addPersonOccasionProfEmployerUVT
}),
...(isPrivate && {
PrivPersonStreet: this.state.addPersonOccasionPrivPersonStreet,
PrivPersonPLZ: this.state.addPersonOccasionPrivPersonPLZ,
PrivPersonCity: this.state.addPersonOccasionPrivPersonCity
})
};
let CombinedPersons = [NewPerson];
if (PreviousPersons !== null) {
CombinedPersons = [...PreviousPersons, ...CombinedPersons]
}
You seem to be mixing up arrays and objects in this case. You want all the properties of a person isolated to a single entity. Object works out best in such cases.
var NewPerson = {
Firstname: this.state.addPersonFirstname,
Lastname: this.state.addPersonLastname,
Birthday: this.state.addPersonBirthday,
Occasion: this.state.addPersonOccasion,
};
if (this.state.addPersonOccasion === 'OccasionProfessional') {
NewPerson = {
...NewPerson,
ProfEmployerName: this.state.addPersonOccasionProfEmployerName,
ProfEmployerPLZ: this.state.addPersonOccasionProfEmployerPLZ,
ProfEmployerCity: this.state.addPersonOccasionProfEmployerCity,
ProfEmployerUVT: this.state.addPersonOccasionProfEmployerUVT
}
}
if (this.state.addPersonOccasion === 'OccasionPrivate') {
NewPerson = {
...NewPerson,
PrivPersonStreet: this.state.addPersonOccasionPrivPersonStreet,
PrivPersonPLZ: this.state.addPersonOccasionPrivPersonPLZ,
PrivPersonCity: this.state.addPersonOccasionPrivPersonCity
]
}
var CombinedPersons
if (PreviousPersons === null) {
CombinedPersons = [NewPerson]
} else {
CombinedPersons = [...PreviousPersons, {...NewPerson}]
}
PreviousPersons will be an array of person objects.
You don't need to spread the new properties...
You can:
if (this.state.addPersonOccasion === 'OccasionProfessional') {
NewPerson = {
...NewPerson,
ProfEmployerName: this.state.addPersonOccasionProfEmployerName,
ProfEmployerPLZ: this.state.addPersonOccasionProfEmployerPLZ,
//... and so on
}
}
A combination of all your answers made the final version:
var NewPerson = {
Firstname: this.state.addPersonFirstname,
Lastname: this.state.addPersonLastname,
Birthday: this.state.addPersonBirthday,
SigImage: this.sigPad.getCanvas().toDataURL('image/png'),
Occasion: this.state.addPersonOccasion,
};
if (this.state.addPersonOccasion === 'OccasionProfessional') {
NewPerson = {
...NewPerson,
ProfEmployerName: this.state.addPersonOccasionProfEmployerName,
ProfEmployerPLZ: this.state.addPersonOccasionProfEmployerPLZ,
ProfEmployerCity: this.state.addPersonOccasionProfEmployerCity,
ProfEmployerUVT: this.state.addPersonOccasionProfEmployerUVT
}
}
if (this.state.addPersonOccasion === 'OccasionPrivate') {
NewPerson = {
...NewPerson,
PrivPersonStreet: this.state.addPersonOccasionPrivPersonStreet,
PrivPersonPLZ: this.state.addPersonOccasionPrivPersonPLZ,
PrivPersonCity: this.state.addPersonOccasionPrivPersonCity
}
}
// Save the user input to NewPerson var - End
// Create combined var with PreviousPersons and NewPerson - Start
var CombinedPersons
if (PreviousPersons === null) {
CombinedPersons = [NewPerson]
} else {
CombinedPersons = [ ...PreviousPersons, NewPerson ]
}
// Create combined var with PreviousPersons and NewPerson - End
I have a class "House" like :
class House{
constructor(params){
this.clear();
// this = {...params} // I know that don't work !!!
//--
// if(params.address !== undefined) this.address = {...params.address}
//...
}
clear(){
this.address = {
number: null,
street: null,
zipcode: null,
ton: null,
}
this.access = {
doorcode: null,
stair: null,
}
}
}
I want to create a new instance of House and inject in constructor multiple json like :
const h = new House({address: { /* json */ }, access: { /* json */});
Or only one like :
const h = new House({access: { /* json */});
In constructor, am i obliged to check all values in "params" to insert in good properties (nested object)
I would like to avoid to create other classes like address and access and in the house constructor create new instance of each.
What's the best practice ?
Regards
Using Object.assign() and object destructuring with default parameters in the constructor, you can achieve this quite easily:
class House {
static get defaultAddress () {
return {
number: null,
street: null,
zipcode: null,
town: null
}
}
static get defaultAccess () {
return {
doorcode: null,
stair: null
}
}
constructor({ address = House.defaultAddress, access = House.defaultAccess } = {}) {
this.clear()
Object.assign(this.address, address)
Object.assign(this.access, access)
}
clear () {
const { defaultAddress, defaultAccess } = House
Object.assign(this, { address: defaultAddress, access: defaultAccess })
}
}
// no object
console.log(new House())
// empty object
console.log(new House({}))
// partial object
console.log(new House({ address: { number: 1, street: 'street', zipcode: 12345, town: 'town' } }))
// empty sub-objects
console.log(new House({ address: {}, access: {} }))
// partial sub-objects
console.log(new House({ address: { number: 1, street: 'street' }, access: { doorcode: 321 } }))
// complete object
console.log(new House({ address: { number: 1, street: 'street', zipcode: 12345, town: 'town' }, access: { doorcode: 321, stair: 3 } }))
.as-console-wrapper{min-height:100%!important}
You can loop through the parameters and set them manually. Then, to clear, remove all own properties (properties that aren't inherited).
class House {
constructor(params) {
// set data
Object.assign(this, params);
}
clear() {
for (let key in this) {
if (this.hasOwnProperty(key))
this[key] = undefined; // or `delete this[key];`
}
}
}
let house = new House({type: "normal", height: 40});
console.log(house, house instanceof House);
Of course, you probably want to limit the input keys to a predefined set. You could store those keys in a static class variable and use them to loop through the properties in constructor and clear.
class House {
constructor(params) {
// check for invalid properties
Object.keys(params).forEach(key => {
if (!House.keys.includes(key))
throw `Invalid paramater ${key}`;
});
// set data
Object.assign(this, params);
}
clear() {
for (let key in House.keys) {
if (this.hasOwnProperty(key))
this[key] = undefined; // or `delete this[key];`
}
}
}
House.keys = ['type', 'height'];
let house = new House({type: 'normal', height: 40});
console.log(house, house instanceof House);
let error = new House({helloWorld: true});
I think you want a common namespace for your instance properties - similar to React's props pattern - you can also specify defaults for each instance you are creating:
const defaultProps = { address: {}, access: {} };
class House {
constructor(props = {}) {
this.props = {...defaultProps, ...props};
}
clear() {
this.props = {...defaultProps};
}
}
App.Person = Ember.Object.extend({
firstName: "Trek",
});
App.person = App.Person.create({
firstName: undefined
})
console.log(App.person.get('firstName'));
This code will output undefined, but I want somehow to intercept setting of property and if it sets to undefined keep default value "Trek" unchanged.
ember-1.0.0
You can use computed properties:
App.Person = Ember.Object.extend({
_firstName: 'Trek',
firstName: function(key, value) {
if (arguments.length > 1 && typeof value !== 'undefined') {
this.set('_firstName', value);
}
return this.get('_firstName');
}.property('_firstName')
});
App.person = App.Person.create({
firstName: undefined
})
console.log(App.person.get('firstName'));
Use a computed property and a default value
App.Person = Ember.Object.extend({
firstNameDefault:'Trek',
firstName:function(key,value){
if(arguments.length > 1 && typeof value !== 'undefined'){
return value
}
return this.get('firstNameDefault');
}.property('firstNameDefault')
});
App.person = App.Person.create({
firstName: undefined
})
console.log(App.person.get('firstName'));