Working With Dynamic Multidimensional key-value pairs in JSON - javascript

Having a thorny problem and only see similar but also simpler solutions on SO.
Is is possible to generate a dynamic key AND dynamic values using JS/JSON?
For instance, let's say I have JSON like this:
{
"email": "user#someco.com",
"firstname": "Bob",
"lastname": "Smith",
"company": "ACME",
"custom": {
"services": [
{
"name": "svc1",
"desc": "abcdefg",
"selected": "true",
"status": "None"
},
{
"name": "svc2",
"desc": "abcdefg",
"selected": "true",
"status": "None"
},
{
"name": "svc3",
"desc": "abcdefg",
"selected": "false",
"status": "None"
},
{
"name": "svc4",
"desc": "abcdefg",
"selected": "false",
"status": "None"
}
],
"fields": [
{
"name": "Products",
"desc": "abcdef",
"type": "multi",
"values": [
{
"name": "Product1",
"desc": "abcdef"
},
{
"name": "Product2",
"desc": "abcdef"
}
],
"services": [
"svc1",
"svc2",
"svc3"
]
},
{
"name": "Wines",
"desc": "abcdef",
"type": "multi",
"values": [
{
"name": "Wine 1",
"desc": "abcdef"
}
],
"services": [
"svc4"
]
},
{
"name": "Fruits",
"desc": "abcdef",
"type": "multi",
"values": [
{
"name": "Fruit 1",
"desc": "abcdef"
},
{
"name": "Fruit 2",
"desc": "abcdef"
}
],
"services": [
"svc4"
]
}
]
}
};
I need to go into the fields and for each field (products, wines, fruits) see if a given service is contained within so that I can go back and generate a product or wine or fruit for each service that requires it. But I don't want to repeat the services names more than once. The resulting JSON should look something like this:
{"svc1":["Products"], "svc2":["Products"], "svc3":["Products"], "svc4":["Fruits", "Wines"]}
The hope would be that to generate a dynamic list in Angular I can just turn and loop back through this JSON, pulling out the values for each product, fruit, wine, whatever.
I've been trying a lot of nested for loops and the like but whenever I get more than one layer down the dynamism seems to stop. I'm guessing that for this to work I need to move between JS Objects and JSON?
Right now I'm trying something like this, which isn't quite working, stringify or no. And maybe I'm flip-flopping too much between JSON and JS Objects:
var outObj = [];
var fieldItems;
$.each(jsonObj.custom.fields, function(key, item) {
fieldItems = item;
fieldItems.name = item.name;
$.each(fieldItems.services, function(key, item) {
var serviceName = item;
//check to see if the serviceName already exists
if (outObj.indexOf(serviceName) > -1) {
outObj.serviceName.push(fieldItems.name);
} else {
outObj.push(serviceName);
}
});
});
JSON.stringify(outObj);
console.log("outObj " + outObj);
I get "can't read property 'push' of undefined" errors and the like. Seems this should be possible from a single nested loop, but maybe I need to just do two passes? Any other suggestions?

To me it sounds like overcomplicated solution. You can use basic array methods of javascript to filter out required structure. I am not sure what profiling_value in the presented snippet, so I started from the object structure in OP
var desiredResult = jsonObj.custom.services.reduce(function(result, service){
result[service.name] = jsonObj.custom.fields.filter(function(field){
return field.services.indexOf(service.name) >= 0;
}).map(function(field){ return field.name; });
return result;
}, {});
This gives the expected result for mentioned object.
reduce is required to iterate over all services and accumulate result in one object. Then for each service fields are iterated to filter out only those that contain link to this service. And finally list of filtered fields is transformed (map) into list of strings - their names - and inserted into accumulator

Related

Insert key's value in the array of object having same key

Say I've an array of object:
const Info = [{
"id": "1aa2",
"details": [{
"name": "rusty",
"age": "12",
"favourite": [ObjectId("602b696cb783fc15845d015e"), ObjectId("602b696cb783fc15845d0112")]
}]
},
{
"id": "3aa2",
"details": [{
"name": "john",
"age": "122",
"favourite": [ObjectId("602b696cb783fc15845d0112s"), ObjectId("602b696cb783fc15845d01wqs")]
}]
}
]
I want to merge favourite in one array as:
["favourite": [
ObjectId("602b696cb783fc15845d015e"),
ObjectId("602b696cb783fc15845d0112"),
ObjectId("602b696cb783fc15845d0112s"),
ObjectId("602b696cb783fc15845d01wqs")
]
]
I tried using for loop but it's creating nasty nested for loop which is reducing performance a lot.
Basically, you need to iterate through Info collection, iterate through 'details' sub-collection, and copy all data into a new array. After, just create a new structure using favs variable content or paste this code as object value directly.
BTW your result array need's to contain an object at least, like that:
[ { favourite: [...] } ]
About nested structures, you should try https://lodash.com/docs/4.17.15#flatMapDeep (at least just check the code)
const Info = [{
"id": "1aa2",
"details": [{
"name": "rusty",
"age": "12",
"favourite": ['ObjectId("602b696cb783fc15845d015e")', 'ObjectId("602b696cb783fc15845d0112")']
}]
},
{
"id": "3aa2",
"details": [{
"name": "john",
"age": "122",
"favourite": ['ObjectId("602b696cb783fc15845d0112s")', 'ObjectId("602b696cb783fc15845d01wqs")']
}]
}
]
const favs = Info.reduce((acc, item) => {
item.details.forEach(detail => {
acc.push(...detail.favourite);
})
return acc;
}, []);
console.log(favs);

Replacing Data in Array

I am working on an angular application. I have an array as follows:
[{
"Name": "Andy"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda"
}]
I have another array which containes data as follows:
[
{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Billy",
"Id": "2",
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Ciena",
"Id": 5
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}
]
I want a resultant array such that code should check if Name is present in first array, then it should copy data from second array for that Name and push it in resultant array. For example common name between above two array is Andy and Doda, so data from Andy and Doda should be pushed to resultant array as follows:
[{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}]
At run time I may get many names so code should be generic. I was trying following code which I got over stackoverflow itself
this.newArray = _.map(this.resultantArray, item => {
const value = _.find(this.dataArray, ['Name', item]);
const obj = value ? value : {Name: item};
return obj;
});
But this code is not working as expected as it works fine for the first time but when data comes for second time it appends data to previous data. I want array to be populated again freshly every time I send data. Please help
You can do this with vanilla JS no need for lodash. You can first map it and inside that you can find the value from second array otherwise return the current object:
var arrayTwo = [ { "Name": "Andy", "Id": "1", "Time": "2020-06-19T11:02+00:00" }, { "Name": "Billy", "Id": "2", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Ciena", "Id": "5", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Doda", "Id": "4", "Time": "2020-06-19T11:05+00:00" } ];
var arrayOne = [{ "Name": "Andy"}, { "Name": "Bayer"}, { "Name": "James"}, { "Name": "Doda"}];
var result = arrayOne.map(val=>arrayTwo.find(p=>p.Name==val.Name) || val);
console.log(result);
Suppose first array name is First
First : any [] = [{"Name": "Andy"},{"Name": "Bayer"},{ "Name": "James"},{"Name": "Doda"}]
And Second array name is Second
Second : any[] = [{"Name": "Andy","Id": "1","Time": "2020-06-19T11:02+00:00"},{"Name": "Bayer"},{"Name": "James"},{"Name": "Doda","Id": "4","Time": "2020-06-19T11:05+00:00"}]
Now do looping and check each name of first if its exists in second copy from second and push in result array
result : any[] =[];
this.First.forEach((element) => {
let index = this.Second.findIndex((x) => element.Name== x.Name);
if (index > -1) {
let data = {
this.Second[index].Name,
this.Second[index].Id,
this.Second[index].time,
};
this.result.push(data);
}
}

How to combine two JSON objects into an array

I have two JSON files - one pertaining to English Premier League data of 2015-16 and other of 2016-17. The JSON data in each file is as follows (extract only):
{
"name": "English Premier League 2015/16",
"rounds": [
{
"name": "Matchday 1",
"matches": [
{
"date": "2015-08-08",
"team1": {
"key": "manutd",
"name": "Manchester United",
"code": "MUN"
},
"team2": {
"key": "tottenham",
"name": "Tottenham Hotspur",
"code": "TOT"
},
"score1": 1,
"score2": 0
},
and so on... lot of name, matches like this in the rounds array.
The data of 2016-17 is similar to above:
{
"name": "English Premier League 2016/17",
"rounds": [
{
"name": "Matchday 1",
"matches": [
{
"date": "2016-08-13",
"team1": {
"key": "hull",
"name": "Hull City",
"code": "HUL"
},
"team2": {
"key": "leicester",
"name": "Leicester City",
"code": "LEI"
},
"score1": 2,
"score2": 1
},
and so on... lot of name, matches like this in the rounds array.
These two are in two separate json files. I am developing an application in Angular 2 and have used a data service to get this data. That part was successful. However I need to use ngFor for creating rows in table and for that I require this data to be iterable like an array.
I have tried to convert these two json objects into an array but could not succeed.
1 I have initialised an empty array and tried to do array.push for each object. That did not work
arr=[];
if(this.data1516!=null && this.data1516!=undefined) {arr.push(this.data1516);}
if(this.data1617!=null && this.data1617!=undefined) {arr.push(this.data1617);}
2 I tried Object. assign -
this.obj=Object.assign({},this.data1516,this.data1617);
and then tried converting the object into array, even that did not work
All I want the final json single file to be an array of those two objects, like this:
[ {obj1} , {obj2} ]
There was a extra ] inside arr.push
This should work :
arr=[];
if(this.data1516!=null && this.data1516!=undefined) {
arr.push(this.data1516);
}
if(this.data1617!=null && this.data1617!=undefined) {
arr.push(this.data1617);
}
OR Shorter version (ES6)
arr = [ ...this.data1516 , ...this.data1617 ];

Using underscore.js to find values in deeply nested JSON

I'm pretty new to Javascript, and I just learned about underscore.js. I have a deeply nested JSON object, and I need to use underscore to find key/value pairs, which I will then use to populate various HTML tables. If the structure was more shallow, using something like _.pluck would be easy, but I just don't know how to traverse past the first couple of nesting levels (i.e. surveyGDB, table, tablenames). The JSON object comes from an XML that is comprised of multiple nesting structures (mashed up from different database tables).
var JSONData =
"surveyGDB": {
"filename": "..\\Topo\\SurveyGeoDatabase.gdb",
"table": {
"tablename": [
{
"#text": "SurveyInfo\n ",
"record": {
"OBJECTID": "1",
"SiteID": "CBW05583-345970",
"Watershed": "John Day",
"VisitType": "Initial visit",
"SurveyInstrument": "Total Station",
"ImportDate": "2015-07-22T09:08:42",
"StreamName": "Duncan Creek",
"InstrumentModel": "TopCon Magnet v2.5.1",
"FieldSeason": "2015"
}
},
{
"#text": "QaQcPoints\n ",
"record": [
{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tp",
"Count": "357"
},
{
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tb",
"Count": "92"
},
{
"OBJECTID": "3",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "to",
"Count": "8"
},
{
"OBJECTID": "4",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bl",
"Count": "279"
},
{
"OBJECTID": "5",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bf",
"Count": "18"
}
]
},
{
"#text": "QaQcPolygons\n ",
"record": [
{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:43:08",
"SurveyExtentCount": "",
"WaterExtentCount": "",
"ChannelUnitsCount": "",
"ChannelUnitsUnique": ""
},
{
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T13:35:15",
"SurveyExtentCount": "1",
"WaterExtentCount": "1",
"ChannelUnitsCount": "21",
"ChannelUnitsUnique": "21"
}
]
}
]
}
}
}
For instance, I wanted all of the values for 'Code' in the 'QaQCPoints' table, so I tried:
var codes = _.flatten(_.pluck(JSONData.surveyGDB.table.tablename[1].record[0], "Code" ));
console.log(codes);
In the console, this returns an array with a length of 5, but with blank values.
What am I doing wrong?
I'd also rather search for the 'Code' values in the table based on something like the '#text' key value, instead of just using it's position in the object.
If I understood you correctly, you want to always search the record array within JSONData.surveyGDB.table.tablename array for some queries. This means you need to find the record based on some parameter and return something from the found record.
Do note that the record property is sometimes an array and sometimes an object (for table SurveyInfo) in your example so I'll assume you need to take this into account.
You can make a small function to extract data and handle both objects and arrays:
function extract(record, prop) {
if (Array.isArray(record)) {
return _.pluck(record, prop);
} else {
return record[prop];
}
}
Usage example:
I wanted all of the values for 'Code' in the 'QaQCPoints' table.
I'd also rather search for the 'Code' values in the table based on something like the '#text' key value, instead of just using it's position in the object.
To achieve this you first find a record using _.find, and then extract Code values from it using the method above:
var table = JSONData.surveyGDB.table.tablename;
// find an item that has `#text` property equal to `QaQcPoints`
var item = _.find(table, function(r) {
return r['#text'] === 'QaQcPoints';
});
// extract codes from the found item's record property
var code = extract(item.record, 'Code');
// output ["tp", "tb", "to", "bl", "bf"]
Running sample:
var JSONData = {
"surveyGDB": {
"filename": "..\\Topo\\SurveyGeoDatabase.gdb",
"table": {
"tablename": [{
"#text": "SurveyInfo",
"record": {
"OBJECTID": "1",
"SiteID": "CBW05583-345970",
"Watershed": "John Day",
"VisitType": "Initial visit",
"SurveyInstrument": "Total Station",
"ImportDate": "2015-07-22T09:08:42",
"StreamName": "Duncan Creek",
"InstrumentModel": "TopCon Magnet v2.5.1",
"FieldSeason": "2015"
}
}, {
"#text": "QaQcPoints",
"record": [{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tp",
"Count": "357"
}, {
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tb",
"Count": "92"
}, {
"OBJECTID": "3",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "to",
"Count": "8"
}, {
"OBJECTID": "4",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bl",
"Count": "279"
}, {
"OBJECTID": "5",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bf",
"Count": "18"
}]
}, {
"#text": "QaQcPolygons",
"record": [{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:43:08",
"SurveyExtentCount": "",
"WaterExtentCount": "",
"ChannelUnitsCount": "",
"ChannelUnitsUnique": ""
}, {
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T13:35:15",
"SurveyExtentCount": "1",
"WaterExtentCount": "1",
"ChannelUnitsCount": "21",
"ChannelUnitsUnique": "21"
}]
}]
}
}
}
function extract(record, prop) {
if (Array.isArray(record)) {
return _.pluck(record, prop);
} else {
return record[prop];
}
}
var table = JSONData.surveyGDB.table.tablename;
var item = _.find(table, function(r) {
return r['#text'] === 'QaQcPoints';
});
console.dir(item);
var code = extract(item.record, 'Code');
console.log(code);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
You have a two stage problem. Stage one is figuring out which table is QaQcPoints. If that's always JSONData.surveyGDB.table.tablename[1], you're good.
The next stage is getting your data out. You can use native array manipulation most of the time (unless you're on really old browsers). So:
var table = JSONData.surveyGDB.table.tablename[1].record;
var codeArray = table.map(function(val) { return val.Code; });
Will do the trick.

Creating Complex JSON Object using JavaScript Code

I am bit new to JSON world. And I have to use JavaScript to create following type of JSON structure. Not sure how to achieve this. Tried with following code, but unable to add second element("12101") as well as people to JSON Structure is where I am struggling.
var chat = {};
chat = {"101":{}};
chat["101"].people= {};
chat["101"].people = {"L0b12leL-Ar9GYKoAAAC":{}};
chat["101"].people.L0b12leL-Ar9GYKoAAAC = {"name":"vikram#qech.com"};
chat["101"].room= {};
JSON structure to achieve
{
"101": {
"people": {
"L0b12leL-Ar9GYKoAAAC": {
"name": "vikram#qtech.com",
"inroom": "f787f316-6424-491b-b779-cfc396f0f8a1",
"owns": "f787f316-6424-491b-b779-cfc396f0f8a1",
"countrycode": "in",
"device": "desktop",
"roomname": "R1"
},
"qKCglYWI1hRhZUZCAAAD": {
"name": "Ishim",
"inroom": "2e52905d-951c-4990-b9b7-2f3fc0602922",
"owns": "2e52905d-951c-4990-b9b7-2f3fc0602922",
"roomname": "Ra"
}
},
"room": {
"f787f316-6424-491b-b779-cfc396f0f8a1": {
"name": "R1",
"id": "f787f316-6424-491b-b779-cfc396f0f8a1",
"owner": "L0b12leL-Ar9GYKoAAAC",
"people": [
"L0b12leL-Ar9GYKoAAAC"
],
"status": "available"
},
"2e52905d-951c-4990-b9b7-2f3fc0602922": {
"name": "Ra",
"id": "2e52905d-951c-4990-b9b7-2f3fc0602922",
"owner": "qKCglYWI1hRhZUZCAAAD",
"people": [
"qKCglYWI1hRhZUZCAAAD"
],
"status": "available"
}
}
},
"12101": {
"people": {
"K-Ar9GYKoAAAC": {
"name": "Rahul.com",
"inroom": "f787f316-6424-491b-b779-cfc396f0f8a1",
"owns": "f787f316-6424-491b-b779-cfc396f0f8a1",
"countrycode": "in",
"device": "desktop",
"roomname": "R1"
},
"I1hRhZUZCAAAD": {
"name": "Vipul",
"inroom": "2e52905d-951c-4990-b9b7-2f3fc0602922",
"owns": "2e52905d-951c-4990-b9b7-2f3fc0602922",
"roomname": "Ra"
}
},
"room": {
"b779-cfc396f0f8a1": {
"name": "Rahul-R1",
"id": "f787f316-6424-491b-b779-cfc396f0f8a1",
"owner": "L0b12leL-Ar9GYKoAAAC",
"people": [
"L0b12leL-Ar9GYKoAAAC"
],
"status": "available"
},
"b9b7-2f3fc0602922": {
"name": "Vipul-Room1",
"id": "2e52905d-951c-4990-b9b7-2f3fc0602922",
"owner": "qKCglYWI1hRhZUZCAAAD",
"people": [
"qKCglYWI1hRhZUZCAAAD"
],
"status": "available"
}
}
}
}
This is invalid because the property name contains dashes.
chat["101"].people.L0b12leL-Ar9GYKoAAAC = {"name":"vikram#qech.com"};
To access it correctly, put it in quotes
chat["101"].people["L0b12leL-Ar9GYKoAAAC"] = {"name":"vikram#qech.com"};
Use bracket notation as a property accessor like this:
chat["12101"].people = {};
chat["101"].people["L0b12leL-Ar9GYKoAAAC"] = {"name":"vikram#qech.com"};
With it, it’s just a routine piece of work. It probably didn’t work right away since dot notation property access requires a valid identifier name. With bracket notation, you can use any string like "L0b12leL-Ar9GYKoAAAC".
Also note that in JSON, anything works as a property name too, as long as it is put in quotes. {"L0b12leL-Ar9GYKoAC":true} is as valid as {"💖":true}.

Categories

Resources