I am dealing with a complicated NoSQL database, and my goal is to parse the data within it, separate each level of childrens keys, and then use these keys to label columns on a grid. In this way I can visually show a documents Parent -> Child -> Child -> Child relationships.
It might be easier for my to describe the data and the task by showing the data and the expected output.
Please keep in mind this is mock data but the structure and the problem will be seen here.
[
{
"customer_id": 1,
"customer_name": "John",
"customer_phone": "720-222-1111",
"orders": [
{
"order_id": 1,
"total": 500,
"ordered_from": "website",
"products": [
{
"product_id": 1,
"product_price": 400,
"product_name": "The Blaster",
"product_description": "Blasts everyone away! Fun in the pool"
},
{
"product_id": 2,
"product_price": 100,
"product_name": "Water",
"product_description": "Average H20, delivered to your doorstep",
"product_attributes": [
{
"name": "Addon",
"color": "Blue"
}
]
}
]
},
{
"order_id": 2,
"total": 240,
"ordered_from": "app",
"geolocation": "California",
"coupon_code": "5X23A",
"products": [
{
"product_id": 2
}
]
}
]
},
{
"customer_id": 1,
"customer_name": "Alice",
"customer_address": "23 Main Street",
"customer_zipcode": "15234",
"orders": [
{
"order_id": 4,
"total": 100,
"ordered_from": "website",
"products": [
{
"product_id": 1,
"product_price": 100,
"product_name": "Fins",
"category": "Water"
}
]
},
{
"order_id": 2,
"total": 240,
"ordered_from": "app",
"geolocation": "California",
"coupon_code": "5X23A",
"products": [
{
"product_id": 2
}
]
}
]
},
{
"customer_id": 1,
"customer_name": "Colin",
"customer_gender": "Male",
"orders": [
{
"order_id": 1,
"total": 500,
"ordered_from": "website",
"products": [
{
"product_id": 1,
"product_price": 400,
"product_name": "The Blaster",
"product_description": "Blasts everyone away! Fun in the pool"
},
{
"product_id": 2,
"product_price": 100,
"product_name": "Water",
"product_description": "Average H20, delivered to your doorstep",
"product_attributes": [
{
"name": "Addon",
"color": "Blue"
}
]
}
]
},
{
"order_id": 2,
"total": 240,
"ordered_from": "app",
"geolocation": "California",
"coupon_code": "5X23A",
"products": [
{
"product_id": 2
}
]
}
]
}
]
Notice:
- Here we have three NoSql Documents, the parent level objects are customers
- the data within these objects are not standardized. One customer has a "customer_phone" key, while another doesnt. One customer has a "customer_address" key, while the other doesnt.
- The same point continues onto the next key of "orders" which is an array of also none-standardized objects. One order has a "geolocation" key, while the other doesn't.
- This non-standardization of data carries all the way down to the third child, "product_attributes", some products have this key, some don't.
My objects is to get every single possible key in every level of children.
So my expected output would be something like this.
Note: the value in this key:value pair doesn't matter at all. I am only using this object for the key, the value could be literally anything.
[
{
"KEY":"parent",
"customer_id":true,
"customer_phone":true,
"customer_zipcode":true,
"customer_address":true,
"customer_gender":true,
"customer_name":true,
"orders":true,
},
{
"KEY":"parent.orders",
"order_id":true,
"total":true,
"ordered_from":true,
"geolocation":true,
"coupon_code":true,
"products":true,
},
{
"KEY":"parent.orders.products",
"product_id":true,
"product_price":true,
"product_name":true,
"product_description":true,
"product_attributes":true,
"category":true,
},
{
"KEY":"parent.orders.products.attributes",
"name":true,
"color":true
}
]
Once I have this output, I could easily loop through it and use the keys to create columns on a grid component.
My attempt at a solution:
Pass the first shared JSON into this function
traverseTree(rawData: any, assembled: any[]): any {
let AllKeys: any = {};
let results = [];
rawData.forEach((question: any) => {
for (const [key, value] of Object.entries(question)) {
AllKeys[key] = value;
if (
Array.isArray(value) &&
value.length > 0 &&
typeof value[0] === 'object'
) {
console.log('Count ', value);
assembled = this.traverseTree(mockJSON.questionnaire, []);
}
}
});
results.push(AllKeys);
return [results, ...assembled];
}
If I remove the inner most if statement, I will get the expected results but ONLY for the parent, so i'll get the first object that I shared in my last JSON.
Another issue I have is that the data structure might change, and so I cannot predict how many levels of children there would be.
Thank you so much for anyone reading this, it's a real head splitter for me.
CodePudding user response:
Since the structure is predictable it's easier to make the recursion. So we are working on arrays of objects with properties. And a name for each "array" (KEY).
So on every level of array, we collect all the keys into an object. Simple enough. if we find an array, we do the same for it (recursion). Only thing is extra parameter is name of KEY but that is simple to add up.
var data=[{customer_id:1,customer_name:"John",customer_phone:"720-222-1111",orders:[{order_id:1,total:500,ordered_from:"website",products:[{product_id:1,product_price:400,product_name:"The Blaster",product_description:"Blasts everyone away! Fun in the pool"},{product_id:2,product_price:100,product_name:"Water",product_description:"Average H20, delivered to your doorstep",product_attributes:[{name:"Addon",color:"Blue"}]}]},{order_id:2,total:240,ordered_from:"app",geolocation:"California",coupon_code:"5X23A",products:[{product_id:2}]}]},{customer_id:1,customer_name:"Alice",customer_address:"23 Main Street",customer_zipcode:"15234",orders:[{order_id:4,total:100,ordered_from:"website",products:[{product_id:1,product_price:100,product_name:"Fins",category:"Water"}]},{order_id:2,total:240,ordered_from:"app",geolocation:"California",coupon_code:"5X23A",products:[{product_id:2}]}]},{customer_id:1,customer_name:"Colin",customer_gender:"Male",orders:[{order_id:1,total:500,ordered_from:"website",products:[{product_id:1,product_price:400,product_name:"The Blaster",product_description:"Blasts everyone away! Fun in the pool"},{product_id:2,product_price:100,product_name:"Water",product_description:"Average H20, delivered to your doorstep",product_attributes:[{name:"Addon",color:"Blue"}]}]},{order_id:2,total:240,ordered_from:"app",geolocation:"California",coupon_code:"5X23A",products:[{product_id:2}]}]}]
function parse_obj(data) {
var result = {}
function do_level(arr, name) {
name = name || "PARENT";
var level = {
KEY: name
}
arr.forEach(function(obj) {
Object.keys(obj).forEach(function(key) {
var value = obj[key];
if (Array.isArray(value)) {
do_level(value, name "." key)
}
if (typeof value === 'object' && value !== null) {
// no need. but could have.
}
level[key] = true;
})
})
result[name] = Object.assign({}, result[name], level)
}
do_level(data);
return Object.values(result);
}
console.log(parse_obj(data))
.as-console-wrapper {
max-height: 100% !important
}