I have a simple JavaScript nested object as shown below. I don't know how many children will be there, but that's how the nature of the data is that I am receiving.
How can I transform into expected result ?
Raw Data nested json
{
"work": {
"children": {
"abc": {
"label": "Work address",
"name": "address"
},
"xyz": {
"label": "Work phone",
"name": "phone"
},
"efg": {
"children": {
"position": {
"label": "Work",
"name": "position"
},
"employees": {
"label": "Number of employees",
"name": "employees"
}
}
}
}
}
}
Expected
{
work: {
"address": "",
"phone": "",
"details": {
"position": "",
"employees": ""
}
}
}
What I have tried is the following code
var jsonschema = root.json
var newjson = {}
for (let name in jsonschema) {
if (jsonschema.children.length > 0 ) {
//add a empty object to newjson
}
}
You could use a generic map function and a recursive transform function -
const map = (f, t) =>
Object.fromEntries(Object.entries(t).map(([ k, v ]) => [ k, f(v) ]))
const transform = (t = {}) =>
t.name
? ""
: map(transform, t.children || t)
const input =
{"work":{"children":{"address":{"component":"BaseInput","label":"Work address","name":"address"},"phone":{"component":"BaseInput","label":"Work phone","name":"phone"},"details":{"children":{"position":{"component":"BaseInput","label":"Work position","name":"position"},"employees":{"component":"BaseInput","label":"Number of employees","name":"employees"}}}}}}
console.log(transform(input))
Related
Hya 👋
Suppose we have a dynamic object like so:
[
{
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"parent": {
"type": "page_id",
"page_id": "d4b96daf-47a3-4a04-b200"
},
"type": "child_database",
"child_database": {
"title": "Hero"
}
},
{
"object": "page",
"id": "d3022361-96d2-4e15-999e",
"parent": {
"type": "database_id",
"database_id": "089cd0d8-ccbf-4e9e-97a6"
},
},
{
"object": "block",
"id": "a0cba166-1787-4e30-8cc3",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_1",
"heading_1": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}
],
}
},
{
"object": "block",
"id": "dbfdd892-8c04-4de3-bf0e",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_2",
"heading_2": {
"rich_text": [
{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}
],
}
}
]
I would like to reconstruct this object by grouping them based on parent-child like relationship. Since every object has "parent" prop.
The desired result should be like so, where the elements that share the same parent are grouped under child array.
{
"d4b96daf-47a3-4a04-b200": {
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"type": "child_database",
"child": [{
"d3022361-96d2-4e15-999e": {
"object": "page",
"child": [{
"a0cba166-1787-4e30-8cc3": {
"object": "block",
"type": "heading_1",
"heading_1": {
"rich_text": [{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}]
}
}
},
{
"dbfdd892-8c04-4de3-bf0e": {
"object": "block",
"type": "heading_1",
"heading_2": {
"rich_text": [{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}]
}
}
}
]
}
}]
}
}
Current workaround
/**
* Generator that traverses through nested object
*/
function* traverse(xs: any[] = []): any {
for (let x of xs) {
yield x
yield* traverse(x.child || [])
}
}
/**
* If the property exists in the nested object, then return node
*/
const deepFind = (block: any, pred: any) => (obj: any) => {
for (let node of traverse([obj])) {
if (pred(node)) {
return node
}
}
}
const findById = (block: any) => (obj: any) => deepFind(block, (o: any) => o[block.id])(obj)
export default async function group(pages: Page[]) {
// stuck here 🙏
}
You can do this linearly: create a Map id=>object, iterate the list, if the parent is already on the map, add your object to the parent.child, otherwise create a placeholder object with the parent's id.
let m = new Map()
for (let obj of data) {
let dummy = {id: 'dummy', child: []}
let oid = obj.id
m.set(oid, {...dummy, ...obj, ...m.get(oid)})
let pid = obj.parent.page_id // or whatever depending on type
m.set(pid, m.get(pid) ?? dummy)
m.get(pid).child.push(obj)
}
In the end, the m.values() will contain a flat list of objects with child arrays properly populated.
I made this function to filter in deep an array of object, but at the end the result is empty. If i check with console.log inside the if statement , it show me filtered object, but resultPost return me empty value. I also tryed to push inside an empty array the filtered value, but not work.
const post = [{
"name": "new post",
"description": "simple desc",
"taxonomies": {
"categories": {
"0": {
"term_id": 15
},
"1": {
"term_id": 20
},
}
}
},
{
"name": "new post 2",
"description": "simple desc 2",
"taxonomies": {
"categories": {
"0": {
"term_id": 11
},
"1": {
"term_id": 12
},
}
}
}
];
const filterID = [15, 1];
const resultPost = post.filter(post => {
if ((post.taxonomies.categories.filter(postct => postct.term_id === filterID)).length > 0) return post
});
console.log(resultPost);
You could filter the posts by matching the taxonomies.categories[id].term_id as follows:
const post = [{
"name": "new post",
"description": "simple desc",
"taxonomies": {
"categories" : {
"0" : {
"term_id" : 15
},
"1" : {
"term_id" : 20
},
}
}
},
{
"name": "new post 2",
"description": "simple desc 2",
"taxonomies": {
"categories" : {
"0" : {
"term_id" : 11
},
"1" : {
"term_id" : 12
},
}
}
}];
const filterID = [15,1];
const resultPost = post.filter(item => {
const { categories } = item.taxonomies;
return Object.keys(categories).reduce((cats, id) => {
filterID.includes(categories[id].term_id) && cats.push(id);
return cats;
}, []).length
});
console.log( resultPost)
Like said in comments, you can't use filter on an Object.
Try this instead
const resultPost = post.filter(post => {
for (let cat in post.taxonomies.categories) { // one way to loop over objects
// filterId is an array so you can't just use ===
if (filterID.includes(post.taxonomies.categories[cat].term_id)) return true;
}
return false;
});
Inside filter, you can take Object.values of that object and then use some based on your condition to filter out records. Something like this:
const post = [{ "name": "new post", "description": "simple desc", "taxonomies": { "categories": { "0": { "term_id": 15 }, "1": { "term_id": 20 }, } } }, { "name": "new post 2", "description": "simple desc 2", "taxonomies": { "categories": { "0": { "term_id": 11 }, "1": { "term_id": 12 }, } } }];
const filterID = [15,1];
const result = post.filter(p=>Object.values(p.taxonomies.categories).some(n=>filterID.includes(n.term_id)));
console.log(result);
I have this kind of json.
let arr1 = [
{
"packageReference": "1234",
"displayName": "Business",
"description": "Includes...",
"promotion": {
"packageReference": "1234",
"displayName": "$100 Standard",
"optionGroup": [
{
"displayName": "Access",
},
{
"displayName": "Contract"
},
{
"displayName": "Equipment"
},
{
"displayName": "Features"
},
{
"displayName": "Fees",
}
]
}
}
]
I need to remove only the object in the arr1[0].promotion.optionGroup where the displayName is 'Fees' and to return the new object without him.
You could do it by filtering the sub array like so:
let arr1 = [
{
"packageReference": "1234",
"displayName": "Business",
"description": "Includes...",
"promotion": {
"packageReference": "1234",
"displayName": "$100 Standard",
"optionGroup": [
{
"displayName": "Access",
},
{
"displayName": "Contract"
},
{
"displayName": "Equipment"
},
{
"displayName": "Features"
},
{
"displayName": "Fees",
}
]
}
}
];
arr1 = arr1.map(e => {
e['promotion']['optionGroup'] =
e['promotion']['optionGroup'].filter(s => s['displayName'] != 'Fees');
return e;
});
console.log(arr1);
// Get new array without the Fees one
const newGroup = arr1[0].promotion.optionGroup.filter(group => group.displayName !== 'Fees');
// Put new group into the object
arr1[0].promotion.optionGroup = newGroup;
Could also do it without creating a variable, but added it for cleanness.
I am trying to push the values "label" and "link" into an object within "data" where the target is the object with an id that is equal to the "parent" value of another object. These values should be pushed into the "children" property of the matching target object. This does not appear to be working. Any pointers?
var data = [
{
"id": 0,
"label": "example page0",
"link": "/apx/...",
"icon": "..",
"parent": null
"children": null
},
{
"id": 1,
"label": "example page1",
"link": "/apx/...",
"icon": "notes",
"parent": null
"children": null
},
{
"id": 2,
"label": "example page2",
"link": "/apx/....",
"icon": "...",
"parent": null
"children": null
},
{
"id": 3,
"label": "example subpage3",
"link": "/apx/....",
"icon": "...",
"parent": 2
"children": null
},
{
"id": 4,
"label": "example subpage4",
"link": "/apx/....",
"icon": "...",
"parent": 2
"children": null
}]
for (let entry of data) {
if (entry.parent > 0) {
var index = data.findIndex(x => x.id == entry.parent);
data[index].children.push({ label: entry.label, link: entry.link })
}
}
Expected output:
[
{
"id": 0,
"label": "example page0",
"link": "/apx/...",
"icon": "..",
"parent": null
"children": null
},
{
"id": 1,
"label": "example page1",
"link": "/apx/...",
"icon": "notes",
"parent": null
"children": null
},
{
"id": 2,
"label": "example page2",
"link": "/apx/....",
"icon": "...",
"parent": null
"children": [
{ "label": "example subpage3", "link": "/apx/...." },
{ "label": "example subpage4", "link": "/apx/...." }
]
}
]
You can implement it by using Array.prototype.reduce. The reduce will iterate over the data array and find elements having the parent property which are not null and find its parent from the data array by searching with the id property.
Now you need to check whether the children property is existing or not, if not you need to create a new array object and assign to the children property, else just append to existing children array:
const data = [{"id":0,"label":"example page0","link":"/apx/...","icon":"..","parent":null,"children":null},{"id":1,"label":"example page1","link":"/apx/...","icon":"notes","parent":null,"children":null},{"id":2,"label":"example page2","link":"/apx/....","icon":"...","parent":null,"children":null},{"id":3,"label":"example subpage3","link":"/apx/....","icon":"...","parent":2,"children":null},{"id":4,"label":"example subpage4","link":"/apx/....","icon":"...","parent":2,"children":null}]
const res = data.reduce((acc, entry, idx, data) => {
if (entry.parent > 0) {
const matchingParent = data.find(e => e.id === entry.parent);
if (matchingParent) {
const child = {
label: entry.label,
link: entry.link
};
if (matchingParent.children) {
matchingParent.children.push(child)
} else {
matchingParent.children = [child];
}
}
} else {
acc.push(entry);
}
return acc;
}, []);
console.log(res);
You can also do it using a for..of loop also:
const data = [{"id":0,"label":"example page0","link":"/apx/...","icon":"..","parent":null,"children":null},{"id":1,"label":"example page1","link":"/apx/...","icon":"notes","parent":null,"children":null},{"id":2,"label":"example page2","link":"/apx/....","icon":"...","parent":null,"children":null},{"id":3,"label":"example subpage3","link":"/apx/....","icon":"...","parent":2,"children":null},{"id":4,"label":"example subpage4","link":"/apx/....","icon":"...","parent":2,"children":null}];
const acc = [];
for (let entry of data) {
if (entry.parent > 0) {
const matchingParent = data.find(e => e.id === entry.parent);
if (matchingParent) {
const child = {
label: entry.label,
link: entry.link
};
if (matchingParent.children) {
matchingParent.children.push(child)
} else {
matchingParent.children = [child];
}
}
} else {
acc.push(entry);
}
}
console.log(acc);
This is when processing needs to happen in-place. In that case we find elements with non-null parents we can add those as children to the parent element and remove those from the data array using splice.
Iterating backwards as the splice will change the length property of the data array:
const data = [{"id":0,"label":"example page0","link":"/apx/...","icon":"..","parent":null,"children":null},{"id":1,"label":"example page1","link":"/apx/...","icon":"notes","parent":null,"children":null},{"id":2,"label":"example page2","link":"/apx/....","icon":"...","parent":null,"children":null},{"id":3,"label":"example subpage3","link":"/apx/....","icon":"...","parent":2,"children":null},{"id":4,"label":"example subpage4","link":"/apx/....","icon":"...","parent":2,"children":null}];
for (let i = data.length - 1; i>= 0; i--) {
const entry = data[i];
if (entry.parent > 0) {
const matchingParent = data.find(e => e.id === entry.parent);
if (matchingParent) {
const child = {
label: entry.label,
link: entry.link
};
if (matchingParent.children) {
matchingParent.children.push(child)
} else {
matchingParent.children = [child];
}
data.splice(i, 1);
}
}
}
console.log(data);
I want to build an new JSON from existing one. The source has sections and rubrics that I no longer need for a listing. The new object called 'items' should have an array of the items.
The final JSON should be sorted by attribute 'name' and look like
{
"items": [
{
"id": 10000006,
"name": "Boah"
},
{
"id": 10000013,
"name": "Gut"
},
{
"id": 10000003,
"name": "Ipsum"
},
{
"id": 10000001,
"name": "Lorem"
},
{
"id": 10000005,
"name": "Lorum"
},
{
"id": 10000004,
"name": "Name"
},
{
"id": 10000002,
"name": "Stet"
}
]
}
For building the new JSON I get this source:
{
"sections": [
{
"name": "FooBar",
"rubrics": [
{
"name": "Foo",
"items": [
{
"id": 10000001,
"name": "Lorem"
},
{
"id": 10000002,
"name": "Stet"
},
{
"id": 10000003,
"name": "Ipsum"
}
]
},
{
"name": "Bar",
"items": [
{
"id": 10000004,
"name": "Name"
},
{
"id": 10000005,
"name": "Lorum"
},
{
"id": 10000006,
"name": "Boah"
}
]
}
]
},
{
"name": "BlahBloob",
"rubrics": [
{
"name": "Bla",
"items": [
{
"id": 10000013,
"name": "Gut"
}
]
},
{
"name": "Bloob",
"items": [
{
"id": 10000014,
"name": "Name"
},
{
"id": 10000015,
"name": "Lorem"
}
]
}
]
}
]
}
What do you think? How can I do this with plain JavaScript or maybe TypeScript?
Thanks for reading and have time for my question. And thanks for reply in advance.
Here you go. You just need to iterate over each rubric of each section of your source to get the items. At the end, sort your list of items by items, and you're done.
This example uses ES6 syntax, but it's easy to convert it to ES5 if needed.
function extractItems(source) {
const items = [];
for (const section of source.sections) {
for (const rubric of section.rubrics) {
items.push(...rubric.items);
}
}
items.sort((a, b) => a.name.localeCompare(b.name));
return { items };
}
A more functional approach use map and reduce to pick the rubrics and merge them.
data.sections
.map(section => section.rubrics) // get rubrics
.reduce((a, b) => a.concat(b)) // merge rubrics
.map(rubric => rubric.items) // get items from each rubric
.reduce((a, b) => a.concat(b)) // merge items
.sort((a, b) => a.name.localeCompare(b.name)); // sort
function(oldObj) {
var newObj = {
"items": []
};
oldObj.sections.forEach(function(section) {
section.rubrics.forEach(function(rubric) {
rubric.items.forEach(function(item) {
newObj.items.push(item);
});
});
});
newObj.items = newObj.items.sort(function(a, b) {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return newObj;
}
And simply use JSON.parse() and JSON.stringify() to convert JSON to and from objects.
It might help you
var data ={
"sections": [
{
"name": "FooBar",
"rubrics": [{"name": "Foo", "items": [{"id": 10000001,"name": "Lorem"}, {"id": 10000002,"name": "Stet"}, {"id": 10000003,"name": "Ipsum"}]
}, {
"name": "Bar",
"items": [{
"id": 10000004,
"name": "Name"
}, {
"id": 10000005,
"name": "Lorum"
}, {
"id": 10000006,
"name": "Boah"
}]
}]
}, {
"name": "BlahBloob",
"rubrics": [{
"name": "Bla",
"items": [{
"id": 10000013,
"name": "Gut"
}]
}, {
"name": "Bloob",
"items": [{
"id": 10000014,
"name": "Name"
}, {
"id": 10000015,
"name": "Lorem"
}]
}]
}]
};
var itemObj = {};
var itemArr = [];
var sections = data.sections;
for(var i=0;i<sections.length;i++)
{
for(var j=0;j<sections[i].rubrics.length;j++){
for(var k=0;k<sections[i].rubrics[j].items.length;k++){
var itemObj;
itemObj['id'] = sections[i].rubrics[j].items[k].id;
itemObj['name'] = sections[i].rubrics[j].items[k].name;
itemArr.push(itemObj);
}
}
}
var finalObj = {"items":itemArr};
console.log(finalObj);
JSFiddle