I have 2 sources of data. One of the sources is the "template" to what is acceptable for the data. However, the second source may have a large amount of data that I don't care about (100+ properties in the JSON). Here are the schemas:
// Only store the data we care about. Only a small subset of
// data that I need for this particular dataset.
state = {
isDirty: false,
data: {
name: '',
address: '',
city: '',
state: ''
}
}
The second source will have the 4 attributes in the data schema above (plus many many more I don't care about). Currently, I am assigning them like this:
let updatedData = {};
for(const key in this.state.data) {
updatedData[key] = someDataSource[key];
}
this.state.data = updatedData;
Using ES6, and perhaps destructing, is there a better way to mass assign variables like this?
Thanks again!
EDIT
Added for clarification the assignment after the loop.
Lodash pick can be used to pick specific keys, or helper function can be used for same purpose:
const pick = (obj, keys) => Object.keys(obj)
.filter((key) => keys.indexOf(key) >= 0)
.reduce(
(newObj, key) => Object.assign(newObj, { [key]: obj[key] }),
{}
);
This is already suggested in many related questions. The thing that is specific to this question is:
this.state.data = pick(someDataSource, Object.keys(this.state.data));
Properties can be excluded and modified in the JSON.parse reviver :
var o = JSON.parse('{"a":1, "b":2}', (k, v) => k === 'a' ? void 0 : k === 'b' ? 3 : v)
console.log( o )
A trick you can do (trick because it requires to swallow an error) is to use an non extensible object, using the Object.preventExtensions and then use Object.assign to fill it with data (in a try/catch block).
// Only store the data we care about. Only a small subset of
// data that I need for this particular dataset.
state = {
isDirty: false,
data: {
name: '',
address: '',
city: '',
state: ''
}
}
const newData = {
name:'name',
address:'address',
city:'city',
state:'state',
phone:'phone',
zip:'zip'
}
const updatedData = Object.preventExtensions({...state.data});
try{
Object.assign(updatedData, newData);
} catch(throwaway){};
console.log(updatedData);
And as a function for reuse
function schemaMerge(schema, data) {
const mergedData = Object.preventExtensions({...schema});
try {
Object.assign(mergedData, data);
} catch (throwaway) {};
return ({...mergedData}); // create a new object from the merged one so that it no longer is extensionless
}
// Only store the data we care about. Only a small subset of
// data that I need for this particular dataset.
state = {
isDirty: false,
data: {
name: '',
address: '',
city: '',
state: ''
}
}
const newData = {
name: 'name',
address: 'address',
city: 'city',
state: 'state',
phone: 'phone',
zip: 'zip'
}
const updatedData = schemaMerge(state.data, newData);
state.data = updatedData;
console.log(state.data);
Related
I have a BehaviorSubject that I want to update by passing a Partial<Type> so I have this code. Then the scan handles the merge between new and old data.
personUpdates$ = new BehaviorSubject<Partial<Person>>({});
person$ = this.personUpdates$.pipe(
scan((acc, curr) => ({ ...acc, ...curr }), {
name: '',
firstName: '',
lastName: '',
} as Person)
);
updatePerson = (person: Partial<Person>) => {
this.personUpdates$.next(person);
}
But the problem I have is to access the data in certain places. For example if i subscribe and log person$ in my constructor I can see the whole object no issues. But if I try to access it in other places I only receive the last updated value.
constructor() {
// Always have the full object
this.person$.subscribe((x) => console.log(x));
}
checkValues = () => {
// Only have the last Partial values
this.person$.pipe(first()).subscribe((x) => console.log(x));
};
How can I make sure I always get the whole object?
I have reproduced the issue in a StackBlitz Sandbox
person$ needs to save its calculations for late subscribers,
use shareReplay(1)
personUpdates$ = new BehaviorSubject<Partial<Person>>({});
person$ = this.personUpdates$.pipe(
scan((acc, curr) => ({ ...acc, ...curr }), {
name: '',
firstName: '',
lastName: '',
} as Person),
shareReplay(1)
);
I would like to only add the values to the return object if they hold a value otherwise I don't even want to send them. I know this can be achieved with if statement, but how would I do this if there were even more?
As you can see based on the required key being true, in the code I have a few of these arguments that can but don't actually need to exist. I'd like to avoid adding them if that is true.
I am using MongoDB so if I don't need to add the field I don't want to have to, but the only way I know how to achieve this is by writing a bunch of if statements to check if they exist. Is there a better way to do this?
The reason why MongoDB is relevant is that MongoDB is a no SQL database so just because something is an option doesn't mean it has to exist.
What i tried so far:
I tried returning undefined if the item doesn't exist this does not work however Example Below
resolve: (_, { input: { createdBy, name, date, description, cost, link, weights, eventApplicants } }) => {
return dbMutations.createEvent({
createdBy, name, date, description,
cost: cost ? cost : undefined, link: link ? link : undefined, weights: weights && JSON.parse(weights),
eventApplicants: eventApplicants && JSON.parse(eventApplicants)
})
}
Code:
You really just need to look at the resolve function, but I put the rest of the code there for reference.
createEvent: t.fieldWithInput({
input: {
createdBy: t.input.field({
type: 'mongoId',
required: true
}),
name: t.input.string({
required: true
}),
date: t.input.field({
type: 'Date',
required: true
}),
description: t.input.string({
required: true
}),
cost: t.input.string(),
link: t.input.string(),
weights: t.input.string(),
applicants: t.input.string(),
},
type: 'mongoId',
// #ts-ignore
resolve: (_, {
input: {createdBy, name, date, description, cost, link, weights, applicants}
}) => {
let parsedWeights
let parsedApplicants
if (weights) {
parsedWeights = JSON.parse(weights)
}
if (applicants) {
parsedApplicants = JSON.parse(applicants)
}
return dbMutations.createEvent({
createdBy,
name,
date,
description,
cost,
link,
weights: parsedWeights,
applicants: parsedApplicants
})
}
})
My Idea:
I think what would work is just assigning these variables to an arbitrary object. Once we do that we can do some kind of object map and set the result to the return statement.
Here's a thing that removes null or undefined values. You may want to tweak the condition to take out empty strings or other falsy things.
const clean = obj =>
Object.fromEntries(
Object.entries(obj).filter(([k, v]) => v != null)
);
let o = { a: 0, b: null, c: '' };
console.log( clean(o) )
With it, the resolve function can say...
resolve: (_, {
input: {createdBy, name, date, description, cost, link, weights, applicants}
}) => {
// or promote this in scope to use elsewhere...
const clean = obj =>
Object.fromEntries(
Object.entries(obj).filter(([k, v]) => v != null)
);
const event = clean({
createdBy,
name,
date,
description,
cost,
link,
weights: weights ? JSON.parse(weights) : null,
applicants: applicants ? JSON.parse(applicants) : null
});
console.log(event); // examine this to prove event is the object you want
return dbMutations.createEvent(event)
}
Have long JSON object and small part is below:
const valueObject = {zipCode: 12345, street1: 'street 1', street2: 'street 2',city:'cityname', uniqueNum: '123456789'};
const mappingObject = {address: {pinCode: zipCode, addressLine1: 'street1', addressLine2:'street2', id: 'uniqueNum', city: city}
const formatObject = {address: {pinCode: '', addressLine1: '', addressLine2:'', id: '0', city: ''}
Trying to transform valueObject to formatObject using mappingObject.
(if valueObject has no value then use default value from formatObject)
It doesn't get work. Seems have a object and not an array. Not sure what is wrong here, Or does it need to merge.
Object.keys(formatObject).map((k)=>{k: valueObject[mappingObject[k]] || mappingObject[k]});
Output expected:
{address: {pinCode: 12345, addressLine1: 'street 1', addressLine2: 'street 2',city:'cityname', id: '123456789'};
In case, any better option through ES6 (
Else case, using for loop and prepare object. )
You actually do not need the formatObject here because the mappingObject gives the full transformation schema, i.e. the relation between two structures. formatObject merely serves as an example of what the possible output looks like.
A feasible implementation would be:
function transform(schema, x) {
return Object.fromEntries(Object.entries(schema).map(([k, v]) => {
if (typeof v === 'string') return [k, x[v]]
else if (typeof v === 'object') return [k, transform(v, x)]
else throw new TypeError(`Schema entry cannot have type ${typeof v}`)
}))
}
transform(valueObject, mappingObject)
that works recursively through the schema to transform a flat-structured object (in this case valueObject) to a nested-structured object.
Hi I am developing web application in Angular 5. I am trying to display tree like structure. I am following the
https://github.com/500tech/angular-tree-component/blob/master/example/cli/src/app/async/async.component.ts and
https://angular2-tree.readme.io/docs/async-data-1
In the above example they have given some data and my application works with it. In real time I am using some other data and converting it to exactly the format in the example they have given.
Below is my example.
let results = JSON.parse('{"userid":"e75792f8-cfea-460e-aca2-07a778c92a7c","tenantid":"00000000-0000-0000-0000-000000000000","username":"karthik","emailaddress":"john#krsars.onmicrosoft.com","isallowed":false,"userroles":[{"userroleid":"b81e63d1-09da-4aa0-af69-0f086ddb20b4","userid":"e75792f8-cfea-460e-aca2-07a778c92a7c","roleid":"85d2f668-f523-4b64-b177-b1a78db74234","tenantappid":1,"validfrom":"2018-01-24T00:00:00","validto":"2018-01-24T00:00:00","isactive":true}]}');
for (const key in results) {
if (results[key] instanceof Array) {
const containerTyp2 = {name: '', hasChildren: false,};
containerTyp2.name = key;
containerTyp2.hasChildren = true;
this.nodes.push(containerTyp2);
} else {
const object = {name: ''};
const containerTyp1 = {name: '', children: []};
object.name = results[key];
containerTyp1.name = key;
containerTyp1.children.push(object);
this.nodes.push(containerTyp1);
}
}
console.log(this.nodes);
Below is the sample data from application example.
this.nodes = [
{
name: 'root1',
children: [
{ name: 'child1' }
]
},
{
name: 'root2',
hasChildren: true
},
{
name: 'root3'
}
];
I am converting my raw data to same format to the example above. When I display both data in console both looks similar except id field. Whenever i display example data, I can see id field get added with random value but explicitly they are not adding. Below is the console of example data.
Below is the console of raw data.
Can someone help me to figure out the issue? I am not able to identify what is the reason not to display raw data. Any help would be appreciated. Thank you.
I fixed this issue by adding below line at the end of for loop.
let results=data;
for (const key in results) {
if (results[key] instanceof Array) {
const containerTyp2 = {name: '', hasChildren: false,};
containerTyp2.name = key;
containerTyp2.hasChildren = true;
this.nodes.push(containerTyp2);
} else {
const object = {name: ''};
const containerTyp1 = {name: '', children: []};
object.name = results[key];
containerTyp1.name = key;
containerTyp1.children.push(object);
this.nodes.push(containerTyp1);
}
this.tree.treeModel.update();
I'm trying to quickly pull out ‘value’ property from some objects using destructuring.. is there a simple way to get it from this? I think it might be possible with some complicated destructuring thing i haven’t quite grocked.
I know I could use loops and such, but I'd like to make it a bit more elegant. I'm looking for a non-repetitive, ideally 1-2 line solution. I wanted to use a map, but that only works on an array...
formData = {
name: {val: 'myName', key: 'value', etc: 'more data'}
province: {val: 'myProvince', key: 'value', etc: 'more data'}
dateOfBirth: {val: 'myBDAY!', key: 'value', etc: 'more data'}
}
//desired outcome:
{
name: 'myName',
province: 'myProvince',
dateOfBirth: 'myBDAY!'
}
//attempt 1
let customer = { name, province, dateOfBirth} = formData; //hrm doesn't get me there
Destructuring is used to assign multiple variables from different elements of an array or object, that's not what you're doing. You can just do:
let customer = {
name: formData.name.val,
province: formData.province.val,
dateOfBirth: formData.dateOfBirth.val
}
If you don't want to list all the properties explicitly, just use a loop.
let customer = {};
for (var k of Object.keys(formData)) {
customer[k] = formData[k].val;
}
A hard to read one-liner would be:
let customer = Object.keys(formData).reduce(
(acc, key) => Object.assign(acc, {[key]: formData[key].val}), {});
to grab .val off every value in the object, and return a new object with the same keys.
That's basically the equivalent of:
let customers = {};
for (const key of Object.keys(formData)) customers[key] = formData[key].val;
Since you didn't like Barmar's answer, you can use a combination of Object.keys and the resulting array's reduce method:
let customer = Object.keys(formData).reduce(function(acc, key) {
acc[key] = formData[key].val;
return acc;
}, {});
You said you wanted to use destructuring… so let's do that:
let customer = {};
for (let k in formData) ({[k]: {val: customer[k]}} = formData);
But really, avoid that, and use clear and readable property assignment instead :-)