Better way to map a deep object to new object - javascript

This code works for converting the JSON to an object where each name object turns into the key for either its value, or if it instead has its own element object breaks that out and does the same to its contents.
Is there a better way to do this that would also allow for more extensiblity of the JSON schema?
Is there a way I can get it all down to a simpler function that I can pass the first element and have it convert it down to whatever depth the schema goes?
const fs = require('fs');
{
let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version='1.0'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let depth = 0;
var compiled = {
[scheme.ele.name]: scheme.ele.ele.map(function(i) {
if (typeof i.ele != 'undefined') {
return {
[i.name]: i.ele.map(function(k) {
if (typeof k.ele != 'undefined') {
return {
[k.name]: k.ele.map(function(p) {
if (typeof p.ele != 'undefined') {
return {
[p.name]: p.ele
};
} else {
return {
[p.name]: p.value
};
}
})
};
} else {
return {
[k.name]: k.value
};
}
})
};
} else {
return {
[i.name]: i.value
};
}
})
};
}
console.log(JSON.stringify(compiled, 0, 2));
I should add, this is intended to eventually also apply validation and grab real data when it gets to the string objects.
The output looks like this:
{
"REPORT": [
{
"SEGMENT0": [
{
"NUMBER1": ""
},
{
"NUMBER2": ""
}
]
},
{
"SEGMENT1": [
{
"RECORD1": [
{
"NUMBER1": ""
},
{
"NUMBER2": ""
}
]
}
]
},
{
"SEGMENT2": []
},
{
"SEGMENT3": []
},
{
"SEGMENT4": []
},
{
"SEGMENT5": []
}
]
}

You could destructure the object, get name, ele and value and return a new object with name as key and either an array by mapping the objects of ele or the value.
const
getData = ({ name, ele, value }) => ({
[name]: Array.isArray(ele)
? ele.map(getData)
: value
});
var scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0\'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root,
result = getData(scheme.ele);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Nina's answer is cleaner but this looks a bit more like your code so I figured I'd post it anyway.
let scheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":""},{"name":"NUMBER2","value":"1"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"2"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
let newScheme = JSON.parse('{"$schema":{"root":{"name":"THINGY","dtd":{"name":"DOCTYPE","value":"something.dtd","commentBefore":["?xml version=\'1.0 \'?","Version NULL"]},"ele":{"name":"REPORT","ele":[{"name":"SEGMENT0","ele":[{"name":"NUMBER1","value":"1"},{"name":"NUMBER2","value":"3"}]},{"name":"SEGMENT1","ele":[{"name":"RECORD1","ele":[{"name":"NUMBER1","value":"4"},{"name":"NUMBER2","value":""}]}]},{"name":"SEGMENT2","ele":[]},{"name":"SEGMENT3","ele":[]},{"name":"SEGMENT4","ele":[]},{"name":"SEGMENT5","ele":[]}]}}}}').$schema.root;
//Yay, recursion!
function mapObj(a, o = {}) {
let array = o[a.name] || [];
for (let i = 0; i < a.ele.length; i++) {
let b = a.ele[i];
array[i] = b.ele ?
mapObj(b, array[i]) : {
[b.name]: b.value
};
}
o[a.name] = array;
return o;
}
let obj = mapObj(scheme.ele);
console.log(obj);
console.log(mapObj(newScheme.ele, obj));

Related

i want to skip last property in object and assign its value to previous property

I have one object like below
let a = {
title: {
value:"developer"
}
publishedOn:{
month:{
value:"jan"
}
year:{
value:"2000"
}
}
and i want to convert it like below object
let b = {
title : "Developer"
publishedOn:{
month:"jan",
year:"2000"
}
}
Constrains are we don't know what properties are inside a variable
I have tried iterative method and i though its not the better way
please help me for better solution
function set(path, value) {
var schema = obj;
var pList = path.split('.');
var len = pList.length;
for(var i = 0; i < len-1; i++) {
var elem = pList[i];
if( !payload[elem] ) payload[elem] = {}
payload = payload[elem];
}
payload[pList[len-1]] = value;
console.log(payload);
}
Object.keys(this.formObject).forEach((key)=> {
if (Object.prototype.hasOwnProperty.call(this.formObject, key)) {
this.getPath(this.formObject[key],key).then((data:any)=>{
set(data.path, data.value);
});
}
});
}
async getPath(obj,path) { //publishedOn , month, yeaer
let value = "";
Object.keys(obj).forEach((key)=> {//month
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if(key === "value"){
path = path;
value = obj[key]
}else{
path = path + "." + key; // publishedOn.month
value = obj[key]['value']; // june
}
}
});
return {path,value }
}
You could look ahead for a coming object and take the final value.
function omitLast(object) {
return Object.fromEntries(Object.entries(object).map(([key, value]) => [
key,
Object.values(value).every(item => item && typeof item === 'object')
? omitLast(value)
: Object.values(value)[0]
]));
}
let input = { title: { value: "developer" }, publishedOn: { month: { value: "jan" }, year: { value: "2000" } } };
result = omitLast(input);
console.log(result);
For old browsers.
function omitLast(object) {
return Object.keys(object).reduce(function (r, key) {
r[key] = Object.keys(object[key]).every(function (k) { return object[key][k] && typeof object[key][k] === 'object'; })
? omitLast(object[key])
: object[key][Object.keys(object[key])[0]];
return r;
}, {});
}
let input = { title: { value: "developer" }, publishedOn: { month: { value: "jan" }, year: { value: "2000" } } };
result = omitLast(input);
console.log(result);

Put object in nested object

Ik have to object and I want to combine those together the right way. I use this code to get them together:
return { record, voorraad: resultsr.filter(x => x != null) }
the output of this will be
{
record:{
_id:"5e8c226e62e43e41b59fe3d3",
naam:"Dames fietsen"
},
voorraad:[
{
_id:"5e8cc9e059fcf75489ebac84",
categorie:"5e8c226e62e43e41b59fe3d3",
status:1
}
]
}
But I like to have it this way
{
record:{
_id:"5e8c226e62e43e41b59fe3d3",
naam:"Dames fietsen",
voorraad:[
{
_id:"5e8cc9e059fcf75489ebac84",
categorie:"5e8c226e62e43e41b59fe3d3",
status:1
}
]
}
}
Who can help me?
this way ?
var data = {
"record":{
"_id":"5e8c226e62e43e41b59fe3d3",
"naam":"Dames fietsen"
},
"voorraad":[
{
"_id":"5e8cc9e059fcf75489ebac84",
"categorie":"5e8c226e62e43e41b59fe3d3",
"status":1
}
]
}
data.record.voorraad = data.voorraad
delete data.voorraad
console.log( JSON.stringify( data, 0 ,2))
uses destructuring:
const data = {
record:{
_id:"5e8c226e62e43e41b59fe3d3",
naam:"Dames fietsen"
},
voorraad:[
{
_id:"5e8cc9e059fcf75489ebac84",
categorie:"5e8c226e62e43e41b59fe3d3",
status:1
}
]
};
const { record, voorraad } = data;
const test = { ...record, voorraad: voorraad.filter(x => x != null) }
console.log(test);
// you have these
const record = {"_id":"5e8c226e62e43e41b59fe3d3", "naam":"Dames fietsen"};
const results = [
{
"_id":"5e8cc9e059fcf75489ebac84",
"categorie":"5e8c226e62e43e41b59fe3d3",
"status":1
}
];
// so just do this
record.voorraad = results.filter(x => x);
const returnValue = { record };
// and returnValue is what you want
console.log(returnValue);

Reformatting array of arrays to nested json in Javascript [duplicate]

I have an array like
[
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1",
.
.
.
]
Wherein my first string before | is the parent and the second string before | is the child and the third string after the second | is the subchild
How can I convert this array into an object like
[
{
"id": "parent1",
"children":[
{
"id": "child1",
"children":[
{
"id": "subChild1"
}
]
}
]
}
]
Parent -> child -> subchild object
Based on Sebastian's answer I tried below using typescript
private genTree(row) {
let self = this;
if (!row) {
return;
}
const [parent, ...children] = row.split('|');
if (!children || children.length === 0) {
return [{
id: parent,
children: []
}];
}
return [{
id: parent,
children: self.genTree(children.join('|'))
}];
}
private mergeDeep(children) {
let self = this;
const res = children.reduce((result, curr) => {
const entry = curr;
const existing = result.find((e) => e.id === entry.id);
if (existing) {
existing.children = [].concat(existing.children, entry.children);
} else {
result.push(entry);
}
return result;
}, []);
for (let i = 0; i < res.length; i++) {
const entry = res[i];
if (entry.children && entry.children.length > 0) {
entry.children = self.mergeDeep(entry.children);
}
};
return res;
}
private constructTree(statKeyNames){
let self = this;
const res = this.mergeDeep(statKeyNames.map(self.genTree).map(([e]) => e));
console.log(res);
}
but this gives me:
Cannot read property 'genTree' of undefined" error
Update:
As per Sebastian's comment changed self.genTree to this.genTree.bind(this) and it worked without any issues
You could use a mapper object which maps each object to it's unique path (You could map the object with each id, but id is not unique here). Then reduce each partial item in the array. Set the root object as the initialValue. The accumulator will be the parent object for the current item. Return the current object in each iteration.
const input = [
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent1|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1"
],
mapper = {},
root = { children: [] }
for (const str of input) {
let splits = str.split('|'),
path = '';
splits.reduce((parent, id, i) => {
path += `${id}|`;
if (!mapper[path]) {
const o = { id };
mapper[path] = o; // set the new object with unique path
parent.children = parent.children || [];
parent.children.push(o)
}
return mapper[path];
}, root)
}
console.log(root.children)
You have to use recursion for that. Take a look here:
const arr = [
"parent1|child1|subChild1",
"parent1|child1|subChild2",
"parent|child2|subChild1",
"parent1|child2|subChild2",
"parent2|child1|subChild1",
"parent2|child1|subChild2",
"parent2|child2|subChild1"
];
function genTree(row) {
const [parent, ...children] = row.split('|');
if (!children || children.length === 0) {
return [{
id: parent,
children: []
}];
}
return [{
id: parent,
children: genTree(children.join('|'))
}];
};
function mergeDeep(children) {
const res = children.reduce((result, curr) => {
const entry = curr;
const existing = result.find((e) => e.id === entry.id);
if (existing) {
existing.children = [].concat(existing.children, entry.children);
} else {
result.push(entry);
}
return result;
}, []);
for (let i = 0; i < res.length; i++) {
const entry = res[i];
if (entry.children && entry.children.length > 0) {
entry.children = mergeDeep(entry.children);
}
};
return res;
}
const res = mergeDeep(arr.map(genTree).map(([e]) => e));
console.log(JSON.stringify(res, false, 2));
I used two helpers here: genTree(row) which recursively generates a simple tree from each row, and mergeDeep(children) which reduces the first-level trees in the result of arr.map(genTree).map(([e]) => e), and then iterates over the array and recursively does the same thing to all children of each entry.

How to parse JSON having nested arrays in javascript or jquery

I want to parse JSON like below
{
"nodeId":3892718504,
"root":true,
"subs":[
{
"nodeId":3892717286
},
{
"nodeId":3892716092,
"subs":[
{
"nodeId":3892715856,
"subs":[
{
"nodeId":3892718592,
"subs":[
{
"nodeId":3892717580
}
]
}
]
}
]
},
{
"nodeId":3892717497
}
]
}
Each node can have subs and those subs can have nodes that can have their own subs. all I want is an array having all nodeId, how can I parse this JSON such that an array called nodes_list is populated with all nodeId.
I can use javascript or jquery.
I'm trying the following approach to get an array of nodeId
jQuery.each(response.topology, function(i,obj) {
if(i == "nodeId") {
node_list.push(obj)
}
if(i == "subs"){
jQuery.each(i, function(key,value) {
if(i == "nodeId") {
node_list.push(obj)
}
}
}
});
I just need a little hint on how it can be in an iterative manner.
This can be done with function generators.
Perhaps not the most enjoyable approach, but I'm pretty sure the other solutions will already imply using other ways, so here is a solution using generators.
PS: Beware of browser support: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
const input = {
"nodeId":3892718504,
"root":true,
"subs":[
{
"nodeId":3892717286
},
{
"nodeId":3892716092,
"subs":[
{
"nodeId":3892715856,
"subs":[
{
"nodeId":3892718592,
"subs":[
{
"nodeId":3892717580
}
]
}
]
}
]
},
{
"nodeId":3892717497
}
]
};
function* nodeLookup(obj) {
if (obj.nodeId) yield obj.nodeId;
if (obj.subs) for (var i = 0; i < obj.subs.length; i++) yield *nodeLookup(obj.subs[i]);
};
const node_ids = [...nodeLookup(input)];
console.log(node_ids);
Just use recursion to iterate over subs
var nodeIds = [];
if (data.nodeId) nodeIds.push(data.nodeId);
function fetchNodeIds (subs) {
if (!subs.length) return cb([]);
var abc = [];
subs.forEach(function (sub) {
abc.push(sub.nodeId);
if (sub.subs && sub.subs.length) abc = abc.concat(fetchNodeIds(sub.subs))
});
return abc;
}
nodeIds = nodeIds.concat(fetchNodeIds(data.subs));
console.log('--All nodeIds--', nodeIds)
It's straightforward to do recursively:
const gatherIds = ({nodeId, subs}, results = []) => subs
? [...results, nodeId, ...(subs .flatMap (sub => gatherIds (sub, results) ))]
: [...results, nodeId]
const response = {"nodeId": 3892718504, "root": true, "subs": [{"nodeId": 3892717286}, {"nodeId": 3892716092, "subs": [{"nodeId": 3892715856, "subs": [{"nodeId": 3892718592, "subs": [{"nodeId": 3892717580}]}]}]}, {"nodeId": 3892717497}]}
console .log (
gatherIds (response)
)
If your target environments don't support flatmap, it's easy enough to shim.

How to get values of nested object by a single string key in javascript?

There is an object info which describes as below:-
let info = {
"person" : {
"name" : "Something",
"age" : 1
}
}
I want to access the property name and I want to access it like info["person.name"], how it can be done?
You could use a proxy like this:
const customAccessor = obj => new Proxy(obj, {
set(_, keys, value) {
const recSet = (object, [key, ...remaining], value) => {
if (remaining.length === 0) {
object[key] = value;
} else {
recSet(object[key], remaining, value);
}
}
recSet(_, keys.split('.'), value);
},
get(_, keys) {
const recGet = (object, [key, ...remaining]) => {
if (remaining.length === 0) {
return object[key];
} else {
return recGet(object[key], remaining);
}
}
return recGet(_, keys.split('.'));
}
});
const info = customAccessor({
"person": {
"name": "Something",
"age": 1
}
});
console.log(info['person.age']);
info['person.age'] = 10;
console.log(info['person.age']);
Try this:
function getProperty(obj, property) {
var locArr = property.split("."), returnVal = obj;
for (let i=0; i<locArr.length; i++) {
returnVal = returnVal[locArr[i]]
}
return returnVal
}
console.log(getProperty({
"person" : {
"name" : "Something",
"age" : 1
}
}, "person.name"))
following works
info['person']['name']
info.person['name']
info['person'].name
info.person.name
and if you really want to keep it in one string I suggest writing a small function that splits by "." and then goes through the same steps as above
function getValue(obj,path){
for(let pathPart of path.split('.')){
obj=obj[pathPart];
}
return obj;
}
You can simply use references
let info = {
"person" : {
"name" : "Something",
"age" : 1
}
}
let findByName = (name) => {
let arr = name.split('.')
let ref = info
arr.forEach(e => {
ref = ref[e] ? ref[e]: {}
})
return ref
}
console.log(findByName("person.name"))
console.log(findByName("person.age"))
console.log(findByName("person.age.someProp"))
console.log(findByName("person.someProp"))

Categories

Resources