JavaScript - How to merge/append to an Object/Array - ReactJS - Spread Syntax - javascript

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

Related

How to use object as keys in an elegant hash-style way

I have the following code:
const { query1 } = require('query1')
const { query2 } = require('query2')
const { query3 } = require('query3')
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "Query",
fields: {
query1,
query2,
query3
}
})
});
const permissions = shield(
{
Query: {
query1: user,
query2: user,
query3: admin
}
}
)
(much longer in the reality)
And I'm looking for a way to make it clearer, like:
const { query1 } = require('query1')
const { query2 } = require('query2')
const { query3 } = require('query3')
const declaration = {
query1: user,
query2: user,
query3: admin
}
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "Query",
fields: someMagic(declaration)
})
});
const permissions = shield(
{
Query: declaration
}
)
But here declaration keys are the strings "query1", "query2" and "query3". Not the objects.
With a WeakMap we could have something like:
const declaration = new WeakMap();
declaration.set(query1, user);
declaration.set(query2, user);
declaration.set(query3, admin);
But I find it much less elegant. Is there another way ?
Hope this might help:
/*
queries['query1'] = require('query1')
queries['query2'] = require('query2')
queries['query3'] = require('query3')
*/
let queries = {
query1: { a: { $eq: "I am Query 1" }, permission: "user" }, //user can be String or object or whatever !
query2: { b: { $eq: "I am Query 2" }, permission: "user" },
query3: { c: { $eq: "I am Query 3" }, permission: "admin" },
};
let declaration = {};
let queryNames = Object.keys(queries);
for (let i in queryNames) {
let curQueryName = queryNames[i];
declaration[curQueryName] = queries[curQueryName]["permission"];
//delete queries[curQueryName]["permission"]
}
console.log(JSON.stringify(declaration, null, 2));
Instead of an object, you could have an array of objects, something like this:
const { query1 } = require('query1')
const { query2 } = require('query2')
const { query3 } = require('query3')
var declarations = [
{ query1, permissions: user },
{ query2, permissions: user },
{ query3, permissions: admin }
];
Then to extract fields and permissions objects:
var fields = {};
var queryPermissions = {};
for (let declaration of declarations) {
for (let key of Object.keys(declaration)) {
if (key !== 'permissions') {
fields[key] = declaration[key];
queryPermissions[key] = declaration.permissions;
}
}
}
For example:
const query1 = { query: 'sample query 1' };
const query2 = { query: 'sample query 2' };
const query3 = { query: 'sample query 3' };
const user = 'user';
const admin = 'admin';
var declarations = [
{ query1, permissions: user },
{ query2, permissions: user },
{ query3, permissions: admin }
];
var fields = {};
var queryPermissions = {};
for (let declaration of declarations) {
for (let key of Object.keys(declaration)) {
if (key !== 'permissions') {
fields[key] = declaration[key];
queryPermissions[key] = declaration.permissions;
}
}
}
console.log(fields);
console.log(queryPermissions);
Side Note:
Another advantage of doing it this way is that you can group queries by permissions, for instance:
var declarations = [
{ query1, query2, permissions: user },
{ query3, permissions: admin }
];
It's not possible to construct an object that uses a variable's name as the property name but something else than the variable's value as the value. You'll have to spell them out twice, once for the permissions once for the resolvers:
const queryPermissions: {
'query1': user,
'query2': user,
'query3': admin,
};
const queryResolvers: {
'query1': require('query1').query1,
'query2': require('query2').query2,
'query3': require('query3').query3,
};
The destructured variables from the imports don't really help with anything here. However, if your module structure is really like this, and you're still using Common.js modules, then you can actually derive the queryResolvers object from the property names of the queryPermissions object:
const queryResolvers = Object.fromEntries(Object.keys(queryPermissions).map(fieldName =>
[fieldName, require(fieldName)[fieldName]]
));
This should do the trick:
((function(t,d){
var fields = {}
for (const key in d) {
fields[key] = t[key]
}
return fields
})(this, declaration)
{a, b, c}
Is just shorthand for {a: a, b: b, c: c}
However, Global constants do not become properties of the window object, unlike var variables, so you might need to fiddle a bit with it.

spread and destructuring object

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.

How can I best optimize my angular function?

I need your help, I have a code and I need to optimize it, can anyone know how this could be?
This is my code:
handlerChangeInfo(value, fieldName: string) {
if (fieldName === 'fullName') {
this.billingFields.full_name = value;
}
if (fieldName === 'address') {
this.billingFields.address = value;
}
if (fieldName === 'postalCode') {
this.billingFields.postal_code = value;
}
if (fieldName === 'city') {
this.billingFields.city = value;
}
if (fieldName === 'stateOrProvince') {
this.billingFields.state_or_province = value;
}
if (fieldName === 'taxId') {
this.billingFields.tax_id = value;
}
}
You can create an object to map your field name values to the object key names
const obj = {
fullName: 'full_name',
address: 'address',
postalCode: 'postal_code',
city: 'city',
stateOrProvince: 'state_or_province',
taxId: 'tax_id'
}
handlerChangeInfo(value, fieldName: string) {
if (obj[fieldName]) {
this.billingFields[obj[fieldName]] = value;
}
}
Map version
const obj = new Map([
['fullName', 'full_name'],
['address', 'address'],
['postalCode', 'postal_code'],
['city', 'city'],
['stateOrProvince', 'state_or_province'],
['taxId', 'tax_id']
]);
handlerChangeInfo(value, fieldName: string) {
if (obj.get(fieldName)) {
this.billingFields[obj.get(fieldName)] = value;
}
}

How to validate deeply nested object structure

I have defined object with nested properties. I want to create a validator function which will check if another object has the same structure and value type as the one that I have defined!
The is the definition of the object:
const OBJECT_SCHEMA = {
name: String,
data: [{
isSelected: Boolean,
mId: String,
mSummary: String,
mMarkets: Array,
mBdd: String,
mReplaceDict: Object,
omId: String,
omnSummary: String,
omnMarkets: Array,
omnBdd: String,
omnReplaceDict: {
id: String,
text: String,
},
}],
metadata: {
emails: Array,
description: String,
},
};
And here is the function that I have for validation. Currently it works only with one nested level! I want it to validate with many nested levels.
function validateObjectStructure(schema, obj) {
let valid = true;
firstLevel: for(const k in schema) {
if(schema[k].constructor === Array) { // if prop is of type array
let i;
for(i = 0; i < schema[k].length; i++) {
for(const kk in schema[k][i]) {
if(!obj[k][i].hasOwnProperty(kk) || obj[k][i][kk].constructor !== schema[k][i][kk]) {
valid = false;
break firstLevel;
}
}
}
}
else if(schema[k].constructor === Object) { // if prop is of type object
for(const kk in schema[k]) {
if(!obj[k].hasOwnProperty(kk) || obj[k][kk].constructor !== schema[k][kk]) {
valid = false;
break firstLevel;
}
}
}
else { // if prop is simple type
if(!obj.hasOwnProperty(k) || obj[k].constructor !== schema[k]) {
valid = false;
break;
}
}
}
return valid;
}
Do you need to work with nested levels of the obj? If yes, you can do something like this instead of the last line:
Object.values(obj).reduce((accValid, value) => {
if (typeof value === 'object') {
return accValid && validateObjectStructure(schema, value);
}
return accValid;
}, valid);
return valid;
Here's a possible implementation:
function validate(obj, schema, path = '') {
let ok = true;
if (!obj)
ok = obj === schema;
else if (typeof schema === 'function')
ok = obj.constructor === schema;
else if (typeof obj !== 'object')
ok = obj === schema;
else if (Array.isArray(schema))
ok = Array.isArray(obj) && obj.every((x, k) => validate(x, schema[0], path + '[' + k + ']'));
else {
let ko = Object.keys(obj);
let ks = Object.keys(schema);
ok = ko.length === ks.length && ks.every(k => validate(obj[k], schema[k], path + '.' + k));
}
if (!ok)
throw new Error('FAILED ' + path);
return true;
}
// example:
const OBJECT_SCHEMA = {
name: String,
data: [{
isSelected: Boolean,
mId: String,
omnReplaceDict: {
id: String,
text: {
deepObj: {
deepProp: [Number]
}
},
},
}],
};
const obj = {
name: "foo",
data: [{
isSelected: true,
mId: "bar",
omnReplaceDict: {
id: "foo",
text: {
deepObj: {
deepProp: [1, 2, "???", 3]
}
},
},
}]
};
validate(obj, OBJECT_SCHEMA)
Note: although this home-made type checker appears to work correctly, it's quite limited (e.g. how to express "array of string-number pairs" or "either null or some object"?), so it might be an option to employ a real one, like Typescript. See here for a possible implementation.

ES6 class default value with array

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

Categories