How to recursively modify nested properties in parsed JSON object - javascript

I have parsed JSON object representing response from old OData(v2) service:
"TimesheetDetailsSet": {
"results": [{
"TimesheetNumber": "1",
(...)
"results": [{
"__metadata":
So as you can see its a complex object with nested objects and arrays. I use SAP UI5 framework and I need to modify it, so there will be no intermediary arrays called "results". I want "TimesheetDetailsSet" and other entitysets containg "results" to be independent arrays of objects.
I wrote this method:
var fnConvert = function (oData) {
if (!oData) {
return oData;
}
for (var sKey in oData) {
if (oData.hasOwnProperty(sKey)) {
if (sKey == 'results') {
var oArr = oData[sKey].reduce(function (acc, cur, i) {
acc[i] = cur;
return acc;
}, []);
delete oData[sKey]
for (var i = 0; i < oArr.length; i++) {
oData[i] = oArr[i];
fnConvert(oData[i]);
}
} else if (typeof oData[sKey] === 'object') {
fnConvert(oData[sKey]);
}
}
}
return oData;
};
The only problem is that need entitysets to be arrays not objects.
Result of the method
UPDATE:
I got this:
"TimesheetDetailsSet": {
"results": [{
"__metadata": {
"id": "...",
"uri": "...",
"type": "ZHR_XSS_ASA_ESS_ODATA_SRV.TimesheetDetails"
},
"TimesheetNumber": "1",
"Username": "DEFAULT_USER",
"TimesheetKey": "\/Date(1529280000000)\/",
"TimesheetDetailKey": "00000001-0000-0000-0000-000000000000",
"CostObject": "0001",
"ConstructionSiteSet": {
"results": [{
"__metadata": {
"id": "...",
"uri": "...",
"type": "ZHR_XSS_ASA_ESS_ODATA_SRV.ConstructionSite"
},
"TimesheetKey": "\/Date(1529280000000)\/",
"WorkingOnConstrSiteSince": "18.08.2018",
"Username": "DEFAULT_USER",
"TimesheetDetailKey": "00000001-0000-0000-0000-000000000000",
"ConstructionSiteKey": "00000100-0000-0000-0000-000000000000",
"ConstructionSiteId": "0001",
"ConstructionSiteDetailSet": {
"results": [{
"__metadata": {
"id": "...",
"uri": "...",
"type": "ZHR_XSS_ASA_ESS_ODATA_SRV.ConstructionSiteDetail"
},
And I want to change it into this:
"TimesheetDetailsSet": [{
"__metadata": {
"id": "...",
"uri": "...",
"type": "ZHR_XSS_ASA_ESS_ODATA_SRV.TimesheetDetails"
},
"TimesheetNumber": "1",
"Username": "DEFAULT_USER",
"TimesheetKey": "\/Date(1529280000000)\/",
"TimesheetDetailKey": "00000001-0000-0000-0000-000000000000",
"CostObject": "0001",
"ConstructionSiteSet": [{
"__metadata": {
"id": "...",
"uri": "...",
"type": "ZHR_XSS_ASA_ESS_ODATA_SRV.ConstructionSite"
},
"TimesheetKey": "\/Date(1529280000000)\/",
"WorkingOnConstrSiteSince": "18.08.2018",
"Username": "DEFAULT_USER",
"TimesheetDetailKey": "00000001-0000-0000-0000-000000000000",
"ConstructionSiteKey": "00000100-0000-0000-0000-000000000000",
"ConstructionSiteId": "0001",
"ConstructionSiteDetailSet": [{
"__metadata": {
"id": "...",
"uri": "...",
"type": "ZHR_XSS_ASA_ESS_ODATA_SRV.ConstructionSiteDetail"
},

Related

How to access a JSON key with .filter()

How would I filter out the following Titles using .filter? I'm expecting the output to be something like: {"Capuchin Monkey", "Capybara"} I'm working with JSON that looks like this:
{
"d": {
"results": [
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Courses": {
"results": [
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Title": "Capuchin Monkey"
},
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Title": "Capybara"
},
JS snippet:
// Courses/Title is what I'm interested in
axios.get([redacted] + "/getByTitle('Categories')/items?$select=Title,Description,Courses/Title,SortOrder&$expand=Courses&$orderby=Title&$top=1000",
{
method: "GET",
credentials: "include",
mode: "no-cors",
headers: {
"Accept": "application/json; odata=verbose"
}
}),
// irrelevant code
]).then(axios.spread((cat, lib, admn) => {
_categories = cat.data.d.results; // -------- //
this.loadCategories();
})).catch(error => {
console.log(error);
});
getCategories(){
return _categories;
}
loadCategories(){
let categs = _categories,
trainingCrs = _categories.d.results.filter(x => {
return {
"crsTitle": x.Courses.results.Title // code smell
}
});
I think you need is map, not filter.
Something like this:
var json = { "results": [
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Courses": {
"results": [
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Title": "Capuchin Monkey"
},
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Title": "Capybara"
}]}}]};
const reducedResult = json.results.reduce((act, val)=> act.concat(val));
const titles = reducedResult.Courses.results.map((value)=>value.Title);
console.log(titles);
To get a list of Titles such as {"Capuchin Monkey", "Capybara"}, you better use Array​.prototype​.map()
rather than Array​.prototype​.filter()
.
var json = {
"d": {
"results": [
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Courses": {
"results": [
{
"__metadata": {
"id": "N/A",
"type" : "N/A"
},
"Title": "Capuchin Monkey"
},
{
"__metadata": {
"id": "N/A",
"type": "N/A"
},
"Title": "Capybara"
}
]
}
}
]
}
}
// Use of .map
trainingCrs = json.d.results[0].Courses.results.map(x => x.Title);
console.log("Training title list: ", trainingCrs);
// Use of .filter
trainingCrs = json.d.results[0].Courses.results.filter(x => x.Title === "Capybara");
console.log("Training list filter on one Title", trainingCrs);
loadCategories(){
let categs = _categories,
trainingCrs = _categories.d.results.map((x) =>x.Courses.results.Title)
});

Need help formatting array in JavaScript

I have a JavaScript array with the following format:
[
{
"header": true,
"id": "0",
"name": "dairy",
},
{
"category": "dairy",
"header": false,
"id": "-LSlje6ESGALGpckMhb7",
"name": "milk",
},
{
"category": "dairy",
"header": false,
"id": "-LSm9EpFg5DhW036aUle",
"name": "cheese",
},
{
"header": true,
"id": "3",
"name": "dessert",
},
{
"category": "dessert",
"header": false,
"id": "-LSm9MLZkrnvtPySw5U6",
"name": "cake",
},
{
"category": "dessert",
"header": false,
"id": "-LSmAQ0rdDLrpz0TSPuD",
"name": "pie",
},
{
"header": true,
"id": "6",
"name": "fruit",
},
{
"category": "fruit",
"header": false,
"id": "-LSlazVIGAKLakxAIa8G",
"name": "apple",
},
{
"category": "fruit",
"header": false,
"id": "-LSlb5GH6xZz-DpNVS22",
"name": "pear",
},
{
"category": "fruit",
"header": false,
"id": "-LSwWJldY1nxQrotyv-V",
"name": "strawberry",
},
{
"header": true,
"id": "10",
"name": "meat",
},
{
"category": "meat",
"header": false,
"id": "-LSljXQzfXthJbOA54Ah",
"name": "fish",
},
{
"category": "meat",
"header": false,
"id": "-LSmA2-R9pOY8abAUyST",
"name": "steak",
},
{
"category": "meat",
"header": false,
"id": "-LSmAJ4J4gIfVQ8sgPDa",
"name": "pork",
},
]
What I am trying to do, is map through this array, and transform it to the following format:
[
{
title: nameOfFirstHeader,
data: items.slice(indexOfFirstHeader, indexOfSecondHeader),
},
{
title: nameOfSecondHeader,
data: items.slice(indexOfSecondHeader, indexOfThirdHeader),
},
{
title: nameOfThirdHeader,
data: items.slice(indexOfThirdHeader, indexOfFourthHeader),
},...and so on
]
So basically there will be an object section for each 'header' that is found in the original array. Each object section data property will contain the items found between the first header and the second header, and so on, until there are no more headers. I really can't wrap my head around how I can do this. Here is a reference to the the module I am using: https://github.com/saleel/react-native-super-grid#sectiongrid-example
Thanks!
I think this may be what you're trying to accomplish...
var grouped = items.reduce((acc,obj)=>{
let {header, name} = obj;
if (header) return [...acc, { title:name, data:[] }] // either first matching header or new match. Add fresh 'header' object
if (!acc.length) return acc; //not header and none have passed. Do nothing
let allButLast = acc.slice(0, acc.length-1),
lastElem = acc[acc.length-1]; // not a header, but there is an existing match. Add it to last match's data array
return [
...allButLast,
{
...lastElem,
data:[...lastElem.data, obj]
}
]
},[])
but it seems unreliable to trust the order of an array for this purpose. It would probably be more reliable to match by isHeader.name === notHeader.category to be less presumptive about the order of data you're iterating over. Like this...
var grouped = items.reduce((acc,obj)=>{
let {header, name, category} = obj;
if (header) return [...acc, { title:name, data:[] }];
if (!acc.length) return acc;
return acc.map((elem)=>{
if (elem.title !== category) return elem;
return {
...elem,
data: [ ...elem.data, obj]
};
})
},[])
I think you can probably do something like
const data = [];
let activeIndexForData = -1;
for(let i = 0; i < dataToSort.length -1; i++) {
if(dataToSort[i].header) {
activeIndexForData++;
}
if(data.length < activeIndexForData - 1) {
data.push({ title: dataToSort[i].name, data# []})
}
else {
data[activeIndexForData].data.push({ title: dataToSort[i].name, data: [])
}
}

Iterate and group the objects using map function

Check for the decimal id and group them accordingly.
Below are the sample and recommended JSON's
Sample JSON
{
"results": [
{
"name": "Download",
"id": "1.1.1"
},
{
"name": "Download",
"id": "1.2"
},
{
"name": "Download",
"id": "1.3.2"
},
{
"name": "Download",
"id": "2"
},
{
"name": "Download",
"id": "2.3"
},
{
"name": "Download",
"id": "3.2"
},
{
"name": "Download",
"id": "3.5"
},
{
"name": "Download",
"id": "4.2"
}
]
}
Would like to iterate and Re-structure the above JSON into below recommended format.
Logic: Should check the id(with and without decimals) and group them based on the number.
For Example:
1, 1.1, 1.2.3, 1.4.5 => data1: [{id: 1},{id: 1.1}....]
2, 2.3, 2.3.4 => data2: [{id: 2},{id: 2.3}....]
3, 3.1 => data3: [{id: 3},{id: 3.1}]
Recommended JSON
{
"results": [
{
"data1": [
{
"name": "Download",
"id": "1.1.1"
},
{
"name": "Download",
"id": "1.2"
},
{
"name": "Download",
"id": "1.3.2"
}
]
},
{
"data2": [
{
"name": "Download",
"id": "2"
},
{
"name": "Download",
"id": "2.3"
}
]
},
{
"data3": [
{
"name": "Download",
"id": "3.2"
},
{
"name": "Download",
"id": "3.5"
}
]
},
{
"data4": [
{
"name": "Download",
"id": "4.2"
}
]
}
]
}
I have tried the below solution but it doesn't group the object
var formatedJSON = [];
results.map(function(d,i) {
formatedJSON.push({
[data+i]: d
})
});
Thanks in advance.
You can use reduce like this. The idea is to create a key-value pair for each data1, data2 etc so that values in this object are the values you need in the final array. Then use Object.values to get those as an array.
const sampleJson = {"results":[{"name":"Download","id":"1.1.1"},{"name":"Download","id":"1.2"},{"name":"Download","id":"1.3.2"},{"name":"Download","id":"2"},{"name":"Download","id":"2.3"},{"name":"Download","id":"3.2"},{"name":"Download","id":"3.5"},{"name":"Download","id":"4.2"}]}
const grouped = sampleJson.results.reduce((a, v) => {
const key = `data${parseInt(v.id)}`;
(a[key] = a[key] || {[key]: []})[key].push(v);
return a;
},{});
console.log({results: Object.values(grouped)})
One liner / Code-golf:
let s={"results":[{"name":"Download","id":"1.1.1"},{"name":"Download","id":"1.2"},{"name":"Download","id":"1.3.2"},{"name":"Download","id":"2"},{"name":"Download","id":"2.3"},{"name":"Download","id":"3.2"},{"name":"Download","id":"3.5"},{"name":"Download","id":"4.2"}]},k;
console.log({results:Object.values(s.results.reduce((a,v)=>(k=`data${parseInt(v.id)}`,(a[k] = a[k]||{[k]:[]})[k].push(v),a),{}))})
Here you go:
var data = {
"results": [
{
"name": "Download",
"id": "1.1.1"
},
{
"name": "Download",
"id": "1.2"
},
{
"name": "Download",
"id": "1.3.2"
},
{
"name": "Download",
"id": "2"
},
{
"name": "Download",
"id": "2.3"
},
{
"name": "Download",
"id": "3.2"
},
{
"name": "Download",
"id": "3.5"
},
{
"name": "Download",
"id": "4.2"
}
]
};
let newSet = new Set();
data.results.forEach(e => {
let key = e.id.substring(0, e.id.indexOf('.'));
console.log(key);
if (newSet.has(key) == false) {
newSet.add(key);
newSet[key] = [];
}
newSet[key].push(e.id);
});
console.log(newSet);
Here's how you'd do it:
var data = {
"results": [
{
"name": "Download",
"id": "1.1.1"
},
{
"name": "Download",
"id": "1.2"
},
{
"name": "Download",
"id": "1.3.2"
},
{
"name": "Download",
"id": "2"
},
{
"name": "Download",
"id": "2.3"
},
{
"name": "Download",
"id": "3.2"
},
{
"name": "Download",
"id": "3.5"
},
{
"name": "Download",
"id": "4.2"
}
]
};
var newData = {
"results": {}
};
data.results.forEach(item => {
var num = item.id.slice(0, 1);
if (newData.results["data" + num]) {
newData.results["data" + num].push(item);
} else {
newData.results["data" + num] = [item];
}
})
data = newData;
console.log(data);
What this does is it iterates through each item in results, gets the number at the front of this item's id, and checks if an array of the name data-{num} exists. If the array exists, it's pushed. If it doesn't exist, it's created with the item.
let input = getInput();
let output = input.reduce((acc, curr)=>{
let {id} = curr;
let majorVersion = 'name' + id.split('.')[0];
if(!acc[majorVersion]) acc[majorVersion]= [];
acc[majorVersion].push(curr);
return acc;
},{})
console.log(output)
function getInput(){
return [
{
"name": "Download",
"id": "1.1.1"
},
{
"name": "Download",
"id": "1.2"
},
{
"name": "Download",
"id": "1.3.2"
},
{
"name": "Download",
"id": "2"
},
{
"name": "Download",
"id": "2.3"
},
{
"name": "Download",
"id": "3.2"
},
{
"name": "Download",
"id": "3.5"
},
{
"name": "Download",
"id": "4.2"
}
]
}
One solution with RegEx for finer control as it would differentiate easily between 1 and 11.
Also this will make sure that even if the same version comes in end(say 1.9 in end) it will put it back in data1.
let newArr2 = ({ results }) =>
results.reduce((acc, item) => {
let key = "data" + /^(\d+)\.?.*/.exec(item.id)[1];
let found = acc.find(i => key in i);
found ? found[key].push(item) : acc.push({ [key]: [item] });
return acc;
}, []);

Trying to pick data from a JSON response text

I'm trying to pick some data from my JSON response text which looks like this:
{
"status": "success",
"reservations": [
{
"id": "22959",
"subject": "SubjectName",
"modifiedDate": "2017-04-03T06:04:24",
"startDate": "2017-04-03T12:15:00",
"endDate": "2017-04-03T17:00:00",
"resources": [
{
"id": "17",
"type": "room",
"code": "codeName",
"parent": {
"id": "2",
"type": "building",
"code": "buildingName",
"name": ""
},
"name": ""
},
{
"id": "2658",
"type": "student_group",
"code": "groupCode",
"name": "groupName"
},
{
"id": "2446",
"type": "student_group",
"code": "groupCode",
"name": "groupName"
},
{
"id": "3137",
"type": "realization",
"code": "codeName",
"name": ""
},
{
"id": "3211",
"type": "realization",
"code": "codeName",
"name": "name"
}
],
"description": ""
},
{
"id": "22960",
"subject": "subjectName",
"modifiedDate": "2017-04-04T06:04:33",
"startDate": "2017-04-04T10:00:00",
"endDate": "2017-04-04T16:00:00",
"resources": [
{
"id": "17",
"type": "room",
"code": "codeName",
"parent": {
"id": "2",
"type": "building",
"code": "codeName",
"name": ""
},
"name": ""
},
{
"id": "2658",
"type": "student_group",
"code": "groupCode",
"name": "groupName"
},
{
"id": "2446",
"type": "student_group",
"code": "groupCode",
"name": "groupName"
}
],
"description": ""
}
]
}
I've been trying to use JSON.parse() and go through the response text with a for-loop with no success. I need to pick the subject names, room names, building names and both student_group names.
This is what my code currently looks like:
var getData = {
"startDate":,
"endDate":,
"studentGroup": [
""]
};
var data = new XMLHttpRequest();
data.onreadystatechange = function () {
if (data.readyState == 4 && data.status == 200) {
try {
// Parse JSON
var json = JSON.parse(data.responseText);
// for-loops
for (var i = 0; i < json.reservations.length; i++) {
for (var x = 0; x < json.reservations[i].length;
x++) {
document.getElementById("test").innerHTML =
json.reservations[i].subject;
}
}
} catch (err) {
console.log(err.message);
return;
}
}
};
// JSON query
data.open("POST", "URL", true, "APIKEY", "PASS");
data.setRequestHeader('Content-Type', 'application/json');
data.send(JSON.stringify(getData));
This only prints the last subject name if I have more than 1 of them.
How should I do this?
Once you have your data parsed, forget it once was JSON. Now you have a JavaScript object.
Check data.status to make sure everything went well.
Loop over data.reservations and, inside that, over data.reservations[i].resources.
You should treat your parsed data as an object, so to get you going, this will get all unique student group names from all returned resources:
var studentGroups = [];
for (var i = 0; i < json.reservations.length; i++) {
if(json.reservations[i].resources != null){
for(var j = 0; j < json.reservations[i].resources.length; j++){
var resource = json.reservations[i].resources[j];
if(resource.type === "student_group"){
if(studentGroups.indexOf("groupName"))
studentGroups.push(resource.name);
}
}
}
}
}
Of course I'm not sure in what format you want to get your result (should this be a flat array or maybe another JSON, maybe only first value is important for you?), but I think you should already have an idea how to handle the topic.

Covert JSON objects inside json objects into a JSON Array in Javascript

Hello I have the following JSON structure and I try to make an array of each object inside each object but there is a way to convert it without iterating each element and get every element.
Or maybe using a Javascript function to get object inside objects and convert to an array?
{
"ES": {
"130": {
"code": "A Coruсa",
"name": "A Coruña"
},
"131": {
"code": "Alava",
"name": "Alava"
},
"...": {
"code": "...",
"name": "..."
}
},
"CH": {
"104": {
"code": "AG",
"name": "Aargau"
},
"...": {
"code": "...",
"name": "..."
}
},
"...": {
"...": {
"code": "...",
"name": "..."
}
}
}
This is what I am looking for:
[
{
"code": "A Coruсa",
"name": "A Coruña"
},
{
"code": "Alava",
"name": "Alava"
},
{
"code": "...",
"name": "..."
},
{
"code": "AG",
"name": "Aargau"
},
{
"code": "...",
"name": "..."
},
{
"code": "...",
"name": "..."
}
]
Thanks for your help, I accept any recommendations.
You can use Object.keys(), Array.prototype.reduce() and Array.prototype.map() for iterating over the properties and for assembling the array.
var obj = { "ES": { "130": { "code": "A Coruсa", "name": "A Coruña" }, "131": { "code": "Alava", "name": "Alava" }, }, "CH": { "104": { "code": "AG", "name": "Aargau" } } },
result = Object.keys(obj).reduce(function (r, k) {
return r.concat(Object.keys(obj[k]).map(function (kk) {
return obj[k][kk];
}));
}, []);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
I have created a fiddle to convert the data. look at it
https://jsbin.com/suzice/edit?js,output
var data = {
"ES": {
"130": {
"code": "A Coruсa",
"name": "A Coruña"
},
"131": {
"code": "Alava",
"name": "Alava"
},
"...": {
"code": "...",
"name": "..."
}
},
"CH": {
"104": {
"code": "AG",
"name": "Aargau"
},
"...": {
"code": "...",
"name": "..."
}
}
};
var list = [];
Object.keys(data).forEach(function(key){
Object.keys(data[key]).forEach(function(sub_key){
list.push(data[key][sub_key]);
});
console.log(list);
});

Categories