How to parse json key dot literals - javascript

I have this JSON object, it's a dynamically generated object, I can't control how it's formatted to json. The problem is that the json looks like this:
{
"id": "def",
"name": "def",
"description": {},
"description.shortened": "def",
"description.extended": "def",
"type": "EDIBLE_BOUQUET",
"image": {},
"image.name": "def",
"image.slug": "def",
"image.extension": "PNG",
"state": "FEATURED",
"stock": "def"
}
How would I go about transforming it into:
{
"id": "def",
"name": "def",
"description": {
"shortened": "def",
"extended": "def"
},
"type": "EDIBLE_BOUQUET",
"image": {
"name": "def",
"slug": "def",
"extension": "PNG"
},
"state": "FEATURED",
"stock": "def"
}

let obj = {
"id": "def",
"name": "def",
"description": {},
"description.shortened": "def",
"description.extended": "def",
"type": "EDIBLE_BOUQUET",
"image": {},
"image.name": "def",
"image.slug": "def",
"image.extension": "PNG",
"state": "FEATURED",
"stock": "def"
}
const transformObj = obj => {
return Object.keys(obj).reduce((acc, key) => {
if(key.indexOf('.') >= 0){
const [parentKey, childKey] = key.split('.');
acc[parentKey] = acc[parentKey] || {};
acc[parentKey][childKey] = obj[key];
} else {
acc[key] = obj[key];
}
return acc;
}, {});
}
console.log(transformObj(obj));

Iterate through the keys of object, and transform it.
function transform(obj){
let tmp={}
Object.keys(obj).forEach(k=>{
if(k.includes('.')){
let path=k.split('.')
let x=path.pop()
path.reduce((cur,p)=>{
if(!(p in cur))cur[p]={}
return cur[p]
},tmp)[x]=obj[k]
}
else{
tmp[k]=obj[k]
}
})
return tmp
}
const obj={
a: 1,
'b.c.d': 2
}
console.log(transform(obj)) // {a: 1, b: {c: {d: 2}}}

If you're in for using lodash, _.set does exactly what you're trying to do here:
const source = {"id":"def","name":"def","description":{},"description.shortened":"def","description.extended":"def","type":"EDIBLE_BOUQUET","image":{},"image.name":"def","image.slug":"def","image.extension":"PNG","state":"FEATURED","stock":"def"};
let target = {};
Object.keys(source).forEach(key => {
_.set(target, key, source[key]);
});
console.log(target);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
Just one catch, the parent property should always be before the child properties in your source object, i.e. description should occur before description.shortened.

Fairly straight forward.
Check each property to see if the name contains a ., add the correct property and delete the original.
const src = {
"id": "def",
"name": "def",
"description": {},
"description.shortened": "def",
"description.extended": "def",
"type": "EDIBLE_BOUQUET",
"image": {},
"image.name": "def",
"image.slug": "def",
"image.extension": "PNG",
"state": "FEATURED",
"stock": "def"
};
for (var k in src) {
let index = k.indexOf('.');
if (index > 0){
let [base, prop] = k.split('.');
let value = src[k];
delete src[k]
src[base][prop] = value;
}
}
console.log(src)

Using lodash's set method would be the easiest path.
const json = '{"id": "def","name": "def","description": {},"description.shortened": "def","description.extended": "def","type": "EDIBLE_BOUQUET","image": {},"image.name": "def","image.slug": "def","image.extension": "PNG","state": "FEATURED","stock": "def"}';
const obj = JSON.parse(json);
const newObj = Object.keys(obj).reduce((o, k) => _.set(o, k, obj[k]), {});
console.log(newObj);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.5/lodash.min.js"></script>
A concern should be noted that if the object keys are not guaranteed to be in the order shown, some data may be lost. See the following example.
const json = '{"id": "def","name": "def","description.shortened": "def","description.extended": "def","description": {},"type": "EDIBLE_BOUQUET","image": {},"image.name": "def","image.slug": "def","image.extension": "PNG","state": "FEATURED","stock": "def"}';
const obj = JSON.parse(json);
const newObj = Object.keys(obj).reduce((o, k) => _.set(o, k, obj[k]), {});
console.log(newObj);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.5/lodash.min.js"></script>
Since the set method iterates over the keys in the order provided from the keys method, the last key will set the value to whatever is provided. So, if the description: {} pair follows any previous description.x: y pair then those values will be lost once the empty object is assigned.
A simple fix would be to include a sort to force the empty object pair to be the first key provided. Note however this will also cause the rest of the object to no longer be in the same order as the original.
const json = '{"id": "def","name": "def","description.shortened": "def","description.extended": "def","description": {},"type": "EDIBLE_BOUQUET","image": {},"image.name": "def","image.slug": "def","image.extension": "PNG","state": "FEATURED","stock": "def"}';
const obj = JSON.parse(json);
const newObj = Object.keys(obj).sort().reduce((o, k) => _.set(o, k, obj[k]), {});
console.log(newObj);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.5/lodash.min.js"></script>
If you do need to roll your own then something like the following would suffice:
const json = '{"id": "def","name": "def","description.shortened": "def","description.extended": "def","description": {},"type": "EDIBLE_BOUQUET","image": {},"image.name": "def","image.slug": "def","image.extension": "PNG","state": "FEATURED","stock": "def"}';
const obj = JSON.parse(json);
const newObj = Object.keys(obj).sort().reduce((o, k) => {
const paths = k.split('.');
// Get to currently defined depth of object
let depth = 0;
let oRef = o;
while (oRef.hasOwnProperty(paths[depth])) {
oRef = oRef[paths[depth++]];
}
const val = paths.slice(depth).reduceRight((v, p) => ({[p]: v}), obj[k]);
Object.assign(oRef, val);
return o;
}, {});
console.log(newObj);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.5/lodash.min.js"></script>

Related

how to set object keys to common denominators?

I have an array of objects and want every object in the array to have the same keys.
var obj1 = {"type": "test", "info": "a lot", "value": 7};
var obj2 = {"value": 5};
var obj3 = {"context": "demo", "info": "very long", "value": 3};
var obj4 = {"info": "no way"};
var dataSet = [obj1,obj2,obj3,obj4];
My attempt is to create an array with all possible keys in the first step.
Then loop through that keys array and update the objects if the key was not found.
keys.forEach(function(a,b){
dataSet.forEach(function(c,d){
//key not found
if(a in c === false)
{
//add key to object
dataSet[b][a] = false;
}
});
});
Howewer, it does not seem to work correctly.
This is my output:
after logic: [
{
"type": false,
"info": "a lot",
"value": 7
},
{
"value": 5,
"info": false
},
{
"context": "demo",
"info": "very long",
"value": false
},
{
"info": "no way",
"context": false
}
]
What am I missing there?
var obj1 = {"type": "test", "info": "a lot", "value": 7};
var obj2 = {"value": 5};
var obj3 = {"context": "demo", "info": "very long", "value": 3};
var obj4 = {"info": "no way"};
var dataSet = [obj1,obj2,obj3,obj4];
var keys = [];
console.log("before logic: ", dataSet);
//Step 1: Fill keys array
dataSet.forEach(function(a,b){
Object.keys(a).forEach(function(c,d)
{
//add keys to array if not already exists
if(!keys.includes(c))
{
keys.push(c);
}
});
});
//Step2: loop through keys array and add key to object if not existing
keys.forEach(function(a,b){
dataSet.forEach(function(c,d){
//key not found
if(a in c === false)
{
//add key to object
dataSet[b][a] = false;
}
});
});
console.log("after logic: ", dataSet);
EDIT:
It would be perfect if the keys are always sorted in the same order too.
You can just collect the keys in a Set using flatMap(), and then assign the missing ones using forEach():
const dataSet = [
{"type": "test", "info": "a lot", "value": 7},
{"value": 5},
{"context": "demo", "info": "very long", "value": 3},
{"info": "no way"}
];
const keys = new Set(dataSet.flatMap(Object.keys));
dataSet.forEach((v) => keys.forEach((k) => v[k] = k in v ? v[k] : false));
console.log(dataSet);
To address the additional request of having the keys in the same order, note that historically, JavaScript object properties were unordered, so relying on the order of object properties in JavaScript is almost never a good idea.
That being said, it's hard to get a fixed order when modifying the existing objects, but doable if you create new objects:
const dataSet = [
{"type": "test", "info": "a lot", "value": 7},
{"value": 5},
{"context": "demo", "info": "very long", "value": 3},
{"info": "no way"}
];
const keys = [...new Set(dataSet.flatMap(Object.keys))];
const result = dataSet.map((v) => keys.reduce((a, k) => ({
...a,
[k]: k in v ? v[k] : false
}), {}));
console.log(result);
You can create a 'template' object from the Set of combined keys and then simply Object.assign to this template from each object. This will give you all the properties in a consistent order.
const dataSet = [
{ "type": "test", "info": "a lot", "value": 7 },
{ "value": 5 },
{ "context": "demo", "info": "very long", "value": 3 },
{ "info": "no way" }
];
const template = Object.fromEntries([...new Set(dataSet.flatMap(o => Object.keys(o)))].map(k => [k, false]));
const result = dataSet.map(o => Object.assign({ ...template }, o));
console.log(result)
Alternatively you can create the template by simply merging all the objects in the data set and overwriting a default value.
const dataSet = [
{ "type": "test", "info": "a lot", "value": 7 },
{ "value": 5 },
{ "context": "demo", "info": "very long", "value": 3 },
{ "info": "no way" }
];
const template = Object.assign({}, ...dataSet);
for (const k of Object.keys(template)) {
template[k] = false;
}
const result = dataSet.map(o => Object.assign({ ...template }, o));
console.log(result)
The problem with your code is basically a typo: You're using the wrong index when you set the key:
dataSet[b][a] = false;
b is the index of the key in keys, not the index of the object in dataSet. You don't need to do that indexing at all, just do:
c[a] = false;
It's much easier to follow what you're doing and such when you use meaningful names for variables rather than a, b, c, and d. Here's your code with some reasonable renaming and with the change described above:
var obj1 = { type: "test", info: "a lot", value: 7 };
var obj2 = { value: 5 };
var obj3 = { context: "demo", info: "very long", value: 3 };
var obj4 = { info: "no way" };
var dataSet = [obj1, obj2, obj3, obj4];
var keys = [];
console.log("before logic: ", JSON.stringify(dataSet, null, 4));
//Step 1: Fill keys array
dataSet.forEach(function (obj) {
Object.keys(obj).forEach(function (key) {
//add keys to array if not already exists
if (!keys.includes(key)) {
keys.push(key);
}
});
});
//Step2: loop through keys array and add key to object if not existing
keys.forEach(function (key) {
dataSet.forEach(function (obj) {
//key not found
if (key in obj === false) {
//add key to object
obj[key] = false;
}
});
});
console.log("after logic: ", JSON.stringify(dataSet, null, 4));
.as-console-wrapper {
max-height: 100% !important;
}
But that code can be much simpler using a Set and modern language features:
const obj1 = { type: "test", info: "a lot", value: 7 };
const obj2 = { value: 5 };
const obj3 = { context: "demo", info: "very long", value: 3 };
const obj4 = { info: "no way" };
const dataSet = [obj1, obj2, obj3, obj4];
const keys = new Set(); // *** Use a set
console.log("before logic: ", dataSet);
// Step 1: Fill keys array
for (const obj of dataSet) {
for (const key of Object.keys(obj)) {
keys.add(key);
}
}
// Step2: loop through keys array and add key to object if not existing
for (const key of keys) {
for (const obj of dataSet) {
if (!(key in obj)) {
obj[key] = false;
}
}
}
console.log("after logic: ", JSON.stringify(dataSet, null, 4));
.as-console-wrapper {
max-height: 100% !important;
}
Robby Cornelissen takes it much further, but I wanted to show using simple loops.

Removing unwanted object keys & undefined in Javascript?

This is my array:
const
array1 = [
{
"value": "0",
"name": "5",
"waste": "remove",
"city": "NY"
},
{
"value": "0",
"name": "51",
"waste": "remove",
}
]
So now, i wanted to remove certain and form a new array with objects: For example, i need to remove "Waste & value" and keep rest of the things, so i used this code:
var keys_to_keep = ['name', 'city']
const result = array2.map(e => {
const obj = {};
keys_to_keep.forEach(k => obj[k] = e[k])
return obj;
});
console.log(result)
And it gives a output as
[ { name: '5', city: 'NY' }, { name: '51', city: undefined } ]
Now as you can see city with undefined value, how to remove that ? i mean filter this and just show keys with value,
So my question is how to filter undefined and also is there any other better solution for removing unwanted object keys and showing new array with wanted keys ? or the method am using is performant enough ?
You can check if the value is undefined in your forEach:
const result = array2.map(e => {
const obj = {};
keys_to_keep.forEach(k => {
if (undefined !== e[k]) {
obj[k] = e[k]
}
)
return obj;
});
You can check if e[k] is defined before you add it to obj by checking whether the e object has the property k using .hasOwnProperty():
const array = [{ "value": "0", "name": "5", "waste": "remove", "city": "NY" }, { "value": "0", "name": "51", "waste": "remove", } ];
const keys_to_keep = ['name', 'city'];
const result = array.map(e => {
const obj = {};
keys_to_keep.forEach(k => {
if (e.hasOwnProperty(k))
obj[k] = e[k]
});
return obj;
});
console.log(result)
If the keys you want to remove aren't dynamic, you can also use destructuring assignment to pull out the properties you want to discard, and use the rest syntax to obtain an object without those properties:
const array = [{ "value": "0", "name": "5", "waste": "remove", "city": "NY" }, { "value": "0", "name": "51", "waste": "remove", } ];
const result = array.map(({value, waste, ...r}) => r);
console.log(result)
I am going to answer both the parts. So here are the steps to do that.
Use map() on the main array.
Get entries of each object using Object.entries().
Apply filter() on entires array are remove those entires for which key is not present in keys_to_keep
Now for the second part.
Using keys_to_keep create an object which contain undefined values for each key.
Use map() again on prev result and use Spread operator. First spread the object created above and then spread the original values. This way if any key is not found it will be set to undefined
const
array1 = [
{
"value": "0",
"name": "5",
"waste": "remove",
"city": "NY"
},
{
"value": "0",
"name": "51",
"waste": "remove",
}
]
var keys_to_keep = ['name', 'city']
let obj = Object.fromEntries(keys_to_keep.map(x => [x, undefined]));
const res = array1.map(obj =>
Object.fromEntries(
Object.entries(obj).filter(([k, v]) => keys_to_keep.includes(k))))
.map(x => ({...obj, ...x}))
console.log(res)
You can use .map to iterate over the objects, Object.entries to get the key-value pairs of each item, Object.fromEntries to group them into the resulting objects, and .filter to get only the entries with a key in keys_to_keep and a value that is not undefined:
const array1 = [
{ "value": "0", "name": "5", "waste": "remove", "city": "NY" },
{ "value": "0", "name": "51", "waste": "remove" }
];
var keys_to_keep = ['name', 'city'];
const result = array1.map(item =>
Object.fromEntries(
Object.entries(item).filter(([key, value]) =>
keys_to_keep.includes(key) && value !== undefined
)
)
);
console.log(result)

Create json object dynamic in nodejs

I have json :
{
"fullName": "abc",
"age": 19,
...
}
I want use Nodejs to add element in above json to object named Variables in below json
{
"variables": {
"fullName" : {
"value" : "abc",
"type" : "String"
},
"age": {
"value" : 19,
"type": "Number"
},
...
}
}
Please help me this case!
You can use Object.entries with .reduce()
let data = {
"fullName": "abc",
"age": 19,
}
let result = Object.entries(data).reduce((a, [key, value]) => {
a.variables[key] = { value, type: typeof value}
return a;
}, { variables: {}})
console.log(result);
We can first entries of that object and then map it accordingly after that we convert that object using Object.fromentries. Here is an implementation:
const obj = { "fullName": "abc", "age": 19 };
const result = Object.fromEntries(Object.entries(obj).map(([k,value])=>[k,{value, type:typeof value}]));
console.log({variable:result});
Are you looking for JSON.parse to get a struct from your file, then JSON.stringify to create a json from your struct ?

Create an array of objects from another array of objects

I have an object as follows:
{
"id": 1,
"dataLockVersion": 0,
"auditData": {
"createDate": "2018-09-18T11:41:28.362",
"createUser": "XXX",
"updateDate": null,
"updateUser": null
},
"property1": 14021,
"property2": {...},
"property3": "Obj"
}
And I have an array that contains multiple objects in that format.
I want to create a new array of objects from this array, which will contain objects in this format :
{
"property1": 14021,
"property2": {...},
"property3": "Obj"
}
This is what I tried :
var result = [];
for (i = 0; i < arr.length; i++) {
delete arr[i].auditData;
delete arr[i].dataLockVersion;
delete arr[i].domainObjectDescription;
delete arr[i].id;
result.push(arr[i]);
}
Is there a better way to do this ?
use map and object destructure
const result = array
.map(
({ property1, property2, property3 })
=> ({ property1, property2, property3 }));
You can simply use Array.map() and object destructuring for this:
let arr =[{ "id": 1, "dataLockVersion": 0, "auditData": { "createDate": "2018-09-18T11:41:28.362", "createUser": "XXX", "updateDate": null, "updateUser": null }, "property1": 14021, "property2": {"x" :1}, "property3": "Obj" }, { "id": 1, "dataLockVersion": 0, "auditData": { "createDate": "2018-09-18T11:41:28.362", "createUser": "XXX", "updateDate": null, "updateUser": null }, "property1": 14021, "property2": {"x" :12}, "property3": "Obj" }];
let result = arr.map(({property1,property2,property3})=>Object.assign({},{property1,property2,property3}));
console.log(result);
I'd use lodash.pick as a oneliner clean and efficient solution.
Pretty often it turns out that this kind of logic will be needed in other parts of the app.
In your case it would be:
var newArrayWithPickedProperties = array.map(item => {
return _.pick(item, ['property1', 'property2', 'property3']);
})
If you go this way ensure you import only lodash.pick not entire lodash library.
you can try this:
var data = [
{"id": 1,"dataLockVersion": 0,"auditData": {"createDate": "2018-09-18T11:41:28.362","createUser": "XXX","updateDate": null,"updateUser": null},"property1": 14021,"property2": {},"property3": "Obj"},
{"id": 2,"dataLockVersion": 1,"auditData": {"createDate": "2018-09-18T11:41:28.362","createUser": "YYY","updateDate": null,"updateUser": null},"property1": 140221,"property2": {},"property3": "Obj3"}
];
var res = data.map(function(m){return {property1: m.property1, property2: m.property2, property3: m.property3};})
console.log(res);
Or if you like tricks and all values are string or number or object that contains them, you can use this (in very heavy objects is not recommended):
let data = [
{"id": 1,"dataLockVersion": 0,"auditData": {"createDate": "2018-09-18T11:41:28.362","createUser": "XXX","updateDate": null,"updateUser": null},"property1": 14021,"property2": {},"property3": "Obj"},
{"id": 2,"dataLockVersion": 1,"auditData": {"createDate": "2018-09-18T11:41:28.362","createUser": "YYY","updateDate": null,"updateUser": null},"property1": 140221,"property2": {},"property3": "Obj3"}
];
var res=[];
JSON.stringify(data).replace(/"(property1)"\:(.+?),.+?"(property\d+)"\:(.+?)(?=})/gi, function(a){res.push(JSON.parse("{"+a+"}"));});
console.log(res);
If you have n number of property with fixed 1st 3 keys, you can do destructuring assignment.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
let data = [
{"id": 1,"dataLockVersion": 0,"auditData": {"createDate": "2018-09-18T11:41:28.362","createUser": "XXX","updateDate": null,"updateUser": null},"property1": 14021,"property2": {},"property3": "Obj","property4":"yo","property5":"hey"},
{"id": 2,"dataLockVersion": 1,"auditData": {"createDate": "2018-09-18T11:41:28.362","createUser": "YYY","updateDate": null,"updateUser": null},"property1": 140221,"property2": {},"property3": "Obj3"}
];
const arr=data.map(a=>{
let {id,dataLockVersion,auditData,...props}=a
return props;
}
)
console.log(arr);
If data is from a JSON string, the JSON.parse reviver parameter can be used to exclude properties :
var json = '{"id":1,"dataLockVersion":0,"auditData":{"createDate":"2018-09-18T11:41:28.362","createUser":"XXX","updateDate":null,"updateUser":null},"property1":14021,"property2":"{...}","property3":"Obj"}'
var obj = JSON.parse(json, (key, value) => /id|data/i.test(key) ? void 0 : value)
console.log( obj )

How to transform JavaScript hashmap?

i'm trying to create a <String, Array()> map from a json object.
Imagine i got this json structure:
[
{
"userId": "123123",
"password": "fafafa",
"age": "21"
},
{
"userId": "321321",
"password": "nana123",
"age": "34"
}
]
The map i want to create would be:
key (string), value (array)
{
"userId": [
"123123",
"321321"
],
"password": [
"fafafa",
"nana123"
],
"age": [
"21",
"34"
]
}
Is it possible to do this? :/
Thanks in advance.
Demo
var json = '[{"userId" : "123123", "password": "fafafa", "age": "21"}, {"userId" : "321321", "password" : "nana123", "age" : "34"}]';
var list = JSON.parse(json);
var output = {};
for(var i=0; i<list.length; i++)
{
for(var key in list[i])
{
if(list[i].hasOwnProperty(key))
{
if(typeof output[key] == 'undefined')
{
output[key] = [];
}
output[key].push(list[i][key]);
}
}
}
document.write(JSON.stringify(output));
Outputs:
{"userId":["123123","321321"],"password":["fafafa","nana123"],"age":["21","34"]}
function mergeAttributes(arr) {
return arr.reduce(function(memo, obj) { // For each object in the input array.
Object.keys(obj).forEach(function(key) { // For each key in the object.
if (!(key in memo)) { memo[key] = []; } // Create an array the first time.
memo[key].push(obj[key]); // Add this property to the reduced object.
});
return memo;
}, {});
}
var json = '[{"userId" : "123123", "password": "fafafa", "age": "21"}, {"userId" : "321321", "password" : "nana123", "age" : "34"}]';
mergeAttributes(JSON.parse(json));
// {
// "userId": ["123123", "321321"],
// "password": ["fafafa", "nana123"],
// "age": ["21", "34"]
// }
Javascript's JSON.stringify will help you to convert any JSON compliant object model into a JSON string.

Categories

Resources