Restructuring JSON in JavaScript - javascript

I have this JSON which I'm parsing using NodeJS and it needs to be restructured into the second JSON which I've added below.
In the first JSON, the rows object has two pages objects (any number of pages objects can be present) which contains all the same keys and values with the exception of values and display keys.
{
"pages": [
{
"label": "SomeLabel",
"name": "Some",
"sections": [
{
"type": "Repeat",
"label": "Label 1",
"name": "Name 1",
"rows": [
{
"pages": [
{
"label": "Label 1",
"name": "Name 1",
"sections": [
{
"type": "Flow",
"label": "Label 2",
"name": "Name 2",
"answers": [
{
"label": "Question Label",
"question": "Question",
"values": [
"Value A"
],
"valuesMetadata": [
{
"display": "Display A",
"row": {
"columns": []
}
}
]
}
]
}
]
}
]
},
{
"pages": [
{
"label": "Label 1",
"name": "Name 1",
"sections": [
{
"type": "Flow",
"label": "Label 2",
"name": "Name 2",
"answers": [
{
"label": "Question Label",
"question": "Question",
"values": [
"Value B"
],
"valuesMetadata": [
{
"display": "Display B",
"row": {
"columns": []
}
}
]
}
]
}
]
}
]
}
],
"footer": null
}
]
}
]
}
In the second JSON the rows object has a single pages object, inside of which the values and display keys have multiple values (the non-common values).
{
"pages": [
{
"label": "SomeLabel",
"name": "Some",
"sections": [
{
"type": "Repeat",
"label": "Label 1",
"name": "Name 1",
"rows": [
{
"pages": [
{
"label": "Label 1",
"name": "Name 1",
"sections": [
{
"type": "Flow",
"label": "Label 2",
"name": "Name 2",
"answers": [
{
"label": "Question Label",
"question": "Question",
"values": [
"Value A",
"Value B"
],
"valuesMetadata": [
{
"display": [
"Display A",
"Display B"
],
"row": {
"columns": []
}
}
]
}
]
}
]
}
]
}
],
"footer": null
}
]
}
]
}
So, I want to know the fast and easy steps to do this. Please let me know the process and methods to solve this.
Thanks

If I understand you correctly, you want to combine all pages in a single page that holds all information.
This can be achieved using the Array.reduce function. reduce takes an array and combines all elements to a single value using a function (provided by you) to combine the first two elements until only one is left (i.e. 1 * 2 => new1; new1 * 3 => new2 where * represents your function).
Your problem would look something like this:
rows[0].pages = rows[0].pages.reduce((currentElement, currentState) => {
if (!currentState) { // first iteration return first element but make sure display is an array
currentElement.sections[0].answers[0].valuesMetadata[0].display =
[currentElement.sections[0].answers[0].valuesMetadata[0].display];
return currentElement;
}
// add values of current element to arrays in current state
currentState.sections[0].answers[0].values
.concat(currentElement.sections[0].answers[0].values);
currentState.sections[0].answers[0].valuesMetadata[0].display
.concat(currentElement.sections[0].answers[0].valuesMetadata[0].display);
return currentState;
});
currentElement is the object of the array that is currently reduced, currentState is the intermediate result of the reduction.
PS:
The object looks like you are way too many arrays where you would not need them. The given code snippet works only for the first element in each array (hence the [0]s. If you really do have multiple values in each array you would have to iterate over all of those accordingly.

Related

How to filter a four dimensional array in Javascript

I have the following array:
[
{
"type": "great-grandparent",
"value": "senior one",
"grandparents": [
{
"value": "special grandparent",
"parents": [
{
"value": "special parent",
"children": [
{
"value": "special child 1"
},
{
"value": "special child 2"
}
]
},
{
"value": "special parent 256",
"children": []
}
]
},
{
"value": "special grandparent 2",
"parents": [
{
"value": "happy parent",
"children": [
{
"value": "fluffy child 1"
},
{
"value": "chilled child 2"
}
]
},
{
"value": "special parent 242",
"children": []
}
]
}
]
},
{
"type": "great-grandparent 2",
"value": "the other senior one",
"grandparents": [
{
"value": "special grandparent 2",
"parents": [
{
"value": "special parent 2",
"children": [
{
"value": "peculiar child 1"
},
{
"value": "curious child 2"
}
]
},
{
"value": "special parent 3",
"children": []
}
]
},
{
"value": "special grandparent 3",
"parents": [
{
"value": "happy parent",
"children": [
{
"value": "fast child 1"
},
{
"value": "talkative child 2"
}
]
},
{
"value": "special parent 4",
"children": []
}
]
}
]
},
{
"type": "great-grandparent 3",
"value": "the other slightly senior one",
"grandparents": [
{
"value": "special grandparent 67",
"parents": [
{
"value": "special parent 243",
"children": []
},
{
"value": "special parent 34554",
"children": []
}
]
},
{
"value": "special grandparent 345",
"parents": [
{
"value": "extremely happy parent"
},
{
"value": "special parent 454",
"children": []
}
]
}
]
}
]
I am trying to return a nested array of great-grandparent (containing grandparent, parent and child arrays/objects) objects where children is not an empty array.
So given the above array, I expect an array of 2 elements to be returned with special parent 256, 242, 3, and 4 omitted as they have zero children. The last great-grandparent object has zero children so this great-grandparent object should be completely omitted too
So far I have:
const filtered = greatGrandparents
.filter((greatGrandparentObj) => greatGrandparentObj.grandparents
.filter((grandparentObj) => grandparentObj.parents
.filter((parentObj) => parentObj.children.length !== 0).length).length)
I am getting the expected greatGrandparent objects back but I am also getting back parents with zero children, I want these to be omitted.
Appreciate any guidance

Create treeview from array of objects including id and original path of each element (javascript)

I have json data that looks like this:
{
"domain": {
"id": "40d74976-5db2-4a57-2a62-08d80969933d",
"name": "A",
"categories": {
"items": [
{
"id": "b17e674a-b64d-4224-bf93-016bc07b6349",
"name": "Category A",
"path": "A/Category A"
},
{
"id": "f6490fe4-98b0-4918-a419-0177000227c2",
"name": "Category AA",
"path": "A/Category A/Category AA"
},
{
"id": "092163a5-b96f-4453-8a22-0307c1f1abec",
"name": "Category B",
"path": "A/Category B"
},
{
"id": "78ac6a08-4dc4-4f60-b0fd-0411b9631c4a",
"name": "Category B-2",
"path": "A/Category B/Category B-2"
}
]
}
}
}
And i would like the result to be this:
{
"domain": {
"id" : "40d74976-5db2-4a57-2a62-08d80969933d",
"name": "A",
"categories": {
"items": [
{
"id": "b17e674a-b64d-4224-bf93-016bc07b6349",
"name": "Category A",
"path": "A/Category A"
"children": [
{
"id": "f6490fe4-98b0-4918-a419-0177000227c2",
"name": "Category AA",
"path": "A/Category A/Category AA"
}
]
},
{
"id": "092163a5-b96f-4453-8a22-0307c1f1abec",
"name": "Category B",
"path": "Domain A/Category B"
"children" :[
{
"id": "78ac6a08-4dc4-4f60-b0fd-0411b9631c4a",
"name": "Category B-2",
"path": "A/Category B/Category B-2"
}
]
}
]
}
}
}
I need to convert the json data in this way because i want to display this in the treeview format on my vue application but also retain the original id of each element since i want to implement the feature allow adding children to each specific element if the user needs to. To do that, it would need the original path of that element and the id of the element to which the child is being added
You can use a Map to key (a copy of) your items by their path attribute, and then iterate the items to link each of them with the parent (via lookup in the map).
Finally structure the top-level of the desired output.
Here is how that could be done:
let data = {"domain": {"id": "40d74976-5db2-4a57-2a62-08d80969933d","name": "A","categories": {"items": [{"id": "b17e674a-b64d-4224-bf93-016bc07b6349","name": "Category A","path": "A/Category A"},{"id": "f6490fe4-98b0-4918-a419-0177000227c2","name": "Category AA","path": "A/Category A/Category AA"},{"id": "092163a5-b96f-4453-8a22-0307c1f1abec","name": "Category B","path": "A/Category B"},{"id": "78ac6a08-4dc4-4f60-b0fd-0411b9631c4a","name": "Category B-2","path": "A/Category B/Category B-2"}]}}};
let root = {};
let { id, name, categories: { items } } = data.domain;
let map = new Map(items.map(o => [o.path, {...o}])).set(name, root);
items.forEach(o => {
let parent = map.get(o.path.slice(0, -o.name.length-1));
parent.children = (parent.children || []).concat(map.get(o.path));
});
let result = { id, name, categories: { items: root.children } };
console.log(result);

Most performant way to sort a deeply nested array of objects by another deeply nested array of objects

As an example - I've included a one element array that contains an object that has a Children key, which is an array of objects and each object also has its' own Children key that contains another array.
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
{
"Id": "100",
"DisplayName": "1-2",
},
]
}
]
}
]
There is a second array of objects that I would like to compare the first array of objects to, with the intention of making sure that the first array is in the same order as the second array of objects, and if it is not - then sort until it is.
Here is the second array:
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "100",
"DisplayName": "1-2",
},
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
]
}
]
}
]
The data that this will run on can be up in the tens of thousands - so performance is paramount.
What I'm currently attempting is using a utility method to convert each element of the second array into a keyed object of objects e.g.
{
1: {
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}
]
}
}
This allows fast look up from the top level. I'm wondering if I should continue doing this all the way down or if there is an idiomatic way to accomplish this. I considered recursion as well.
The order of the already sorted array is not based on Id - it is arbitrary. So the order needs to be preserved regardless.
Assuming same depth and all Id's exist in each level of each object use a recursive function that matches using Array#findIndex() in sort callback
function sortChildren(main, other) {
other.forEach((o, i) => {
if (o.children) {
const mChilds = main[i].children, oChilds = o.children;
oChilds.sort((a, b) => {
return mChilds.findIndex(main => main.Id === a.Id) - mChilds.findIndex(main => main.Id === b.Id)
});
// call function again on this level passing appropriate children arrays in
sortChildren(mChilds, oChilds)
}
})
}
sortChildren(data, newData);
console.log(JSON.stringify(newData, null, ' '))
<script>
var data = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "3",
"DisplayName": "1-2",
},
{
"Id": "4",
"DisplayName": "3-4",
},
]
}]
}]
var newData = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}]
}]
</script>

Exclude items from Array.map()

I am trying to exclude items that contain Packages in packageData.type. To show only the packageData that has items with packageData.type as Add-on.
Array is as example
{
"packageData": [
{
"title": "Title 1",
"type": [
"Packages"
]
},
{
"title": "Title 2",
"type": [
"Add-on"
]
},
{
"title": "Title 3",
"type": [
"Add-on"
]
},
{
"title": "Title 4",
"type": [
"Add-on"
]
}
]
}
and this is how I am currently trying to remove results which is giving zero results.
<ul>
{packageData.map(function() {
if (packageData.type ==! "Packages") {
return (
<li key={packageData.id}>
<a>
<img src={packageData.heroImage.sizes.src} alt=""/>
<h3>{packageData.title}</h3>
</a>
</li> );
} else {
return null;
};
})}
</ul>
How can I make this work? Or should I be filtering these results prior to displaying and then map the returned items?
You have to use filter before map, Filter will filter the array and then map will iterate through array. Below is the sample code
var data = {
"packageData": [
{
"title": "Title 1",
"type": [
"Packages"
]
},
{
"title": "Title 2",
"type": [
"Add-on"
]
},
{
"title": "Title 3",
"type": [
"Add-on"
]
},
{
"title": "Title 4",
"type": [
"Add-on"
]
}
]
};
data.packageData.filter((item) => { return item.type[0] !== 'Packages' }).map((item)=> { console.log(item); });

iterate through Json array without having a key

First of all, this question might have been asked several times already, but every thread I found didn't help me. I would appreciate if one could rewrite my function below.
I have the following array with Json objects.
The goal is to get the "url" values. Right now I get every value from that array.
a = [{
"result": [{
"name": [
"name"
],
"url": [
"www.bar.com"
]
},
{
"name": [
"name 2"
],
"url": [
"www.bar.org"
]
},
{
"name": [
"name 1"
],
"url": [
"www.bar.biz"
]
},
{
"name": [
"name 3"
],
"url": [
"www.bar.jp"
]
}
]
}];
document.getElementById("foo").innerHTML = "How to explicitly get the url value?'";
loopThrough(a);
function loopThrough(obj) {
for (var key in obj) {
// skip loop if the property is from prototype
if (!obj.hasOwnProperty(key)) continue;
if (typeof obj[key] !== 'object') {
console.log(obj[key]);
} else {
loopThrough(obj[key]);
}
}
}
<div id="foo">
</div>
How would I access each "url" element in this array?
Maybe there is also a smarter solution than this.
Thank you in advance.
a = [{
"result": [{
"name": [
"name"
],
"url": [
"www.bar.com"
]
},
{
"name": [
"name 2"
],
"url": [
"www.bar.org"
]
},
{
"name": [
"name 1"
],
"url": [
"www.bar.biz"
]
},
{
"name": [
"name 3"
],
"url": [
"www.bar.jp"
]
}
]
}];
loopThrough(a);
// Uses ES6 syntax
function loopThrough(obj) {
obj[0].result.forEach(r => console.log(r.url[0]));
}
try it yourself.
var a = [{
"result": [{
"name": [
"name"
],
"url": [
"www.bar.com"
]
},
{
"name": [
"name 2"
],
"url": [
"www.bar.org"
]
},
{
"name": [
"name 1"
],
"url": [
"www.bar.biz"
]
},
{
"name": [
"name 3"
],
"url": [
"www.bar.jp"
]
}
]
}];
var urls=a[0].result.reduce(function(urls,it){
return urls.concat(it.url);
},[]);
console.log(urls);

Categories

Resources