Related
I am not a seasoned Javascript coder or even too familiar with JSON. My approach is likely very naive. Recommendations for better approaches are welcome.
Here's my code.
<!DOCTYPE html>
<html>
<head>
<script>
database = {
"name": "AdventureWorks2012 vs AdventureWorksModified",
"children": [{
"name": "Person",
"children": [{
"name": "Address",
"children": [{
"name": "AddressID",
"attributes": {
"coltype": "int",
"coldefault": null,
"colordinal": 1,
"colCharLength": null
}
}]
},{
"name": "Address",
"children": [{
"name": "AddressID",
"attributes": {
"coltype": "int",
"coldefault": null,
"colordinal": 1,
"colCharLength": null
}
}]
}]
},{
"name": "PersonDELETEmePLEASE",
"children": [{
"name": "Address",
"children": [{
"name": "AddressID",
"attributes": {
"coltype": "int",
"coldefault": null,
"colordinal": 1,
"colCharLength": null
}
}]
}]
}]
}
console.log(`name : ${database.name}`);
console.log("=SCHEMAS=");
for (var i in database.children){
console.log(`name : ${database.children[i].name}`);
console.log("children: (-TABLES-)");
for (var j in database.children){
console.log(`name : ${database.children[i].children[j].name}`);
console.log("children: (-COLUMNS-)");
for (var k in database.children[i].children[j].children){
console.log(`name : ${database.children[i].children[j].children[k].name}`);
console.log("children: (-DATA-)");
for (var l in database.children[i].children[j].children[k].attributes){
console.log(`${l} : ${database.children[i].children[j].children[k].attributes[l]}`);
}
}
}
console.log("\n\n\n");
}
</script>
</head>
<body>
</body>
</html>
The structure is database -> schemas -> tables -> columns + column data.
AdventureWorks2012 vs AdventureWorksModified -> Person -> Address -> AddressID + attributes.
As mentioned in the title, I am trying to iterate through some JSON data. Whether if outputted to HTML or to the console, I run into an error on a strange condition: whenever a schema has just ONE table. It can have one child, or one data member, but it MUST have more than one table.
This is the error I get:
Please aid my understanding: what's in my looping causes this to happen? I want to ask before I start looking for workarounds and end up leading myself into a ditch.
Thanks for any help.
Your two loops loop over the same thing
for (var i in database.children) { <-- same
for (var j in database.children) <-- same
You forgot to reference the nesting
for (var i in database.children) { <-- first level
for (var j in database.children[i].children) <-- second level
console.log(database.children[i].children[j])
I am using the code below to build a table based on an API and am fine when there is in object in an array (e.g. lineStatuses[0].statusSeverityDescription), however when there is an object, in an object, in an array, it does not work and I get the result [object Object] returned.
Here is a sample of the JSON data from the URL (I am expecting Undefined to be returned for the first record):
[
{
"$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
"id": "bakerloo",
"name": "Bakerloo",
"modeName": "tube",
"disruptions": [],
"created": "2016-06-03T12:36:54.19Z",
"modified": "2016-06-03T12:36:54.19Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
"id": 0,
"statusSeverity": 10,
"statusSeverityDescription": "Good Service",
"created": "0001-01-01T00:00:00",
"validityPeriods": []
}
],
"routeSections": [],
"serviceTypes": [
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Regular",
"uri": "/Line/Route?ids=Bakerloo&serviceTypes=Regular"
}
]
},
{
"$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
"id": "central",
"name": "Central",
"modeName": "tube",
"disruptions": [],
"created": "2016-06-03T12:36:54.037Z",
"modified": "2016-06-03T12:36:54.037Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
"id": 0,
"lineId": "central",
"statusSeverity": 5,
"statusSeverityDescription": "Part Closure",
"reason": "CENTRAL LINE: Saturday 11 and Sunday 12 June, no service between White City and Ealing Broadway / West Ruislip. This is to enable track replacement work at East Acton and Ruislip Gardens. Replacement buses operate.",
"created": "0001-01-01T00:00:00",
"validityPeriods": [
{
"$type": "Tfl.Api.Presentation.Entities.ValidityPeriod, Tfl.Api.Presentation.Entities",
"fromDate": "2016-06-11T03:30:00Z",
"toDate": "2016-06-13T01:29:00Z",
"isNow": false
}
],
"disruption": {
"$type": "Tfl.Api.Presentation.Entities.Disruption, Tfl.Api.Presentation.Entities",
"category": "PlannedWork",
"categoryDescription": "PlannedWork",
"description": "CENTRAL LINE: Saturday 11 and Sunday 12 June, no service between White City and Ealing Broadway / West Ruislip. This is to enable track replacement work at East Acton and Ruislip Gardens. Replacement buses operate.",
"additionalInfo": "Replacement buses operate as follows:Service A: White City - East Acton - North Acton - West Acton - Ealing Common (for District and Piccadilly Lines) - Ealing BroadwayService B: White City - North Acton - Northolt - South Ruislip - Ruislip Gardens - West RuislipService C: White City - North Acton - Park Royal (Piccadilly Line) - Hanger Lane - Perivale - Greenford - Northolt",
"created": "2016-05-12T11:04:00Z",
"affectedRoutes": [],
"affectedStops": [],
"isBlocking": true,
"closureText": "partClosure"
}
}
],
"routeSections": [],
"serviceTypes": [
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Regular",
"uri": "/Line/Route?ids=Central&serviceTypes=Regular"
}
]
}
]
I am also trying to use setInterval to refresh the tube-disruption DIV with updated data from the API, which is not workng. Below is the code (with the URL returning some of the JSON data above). Any ideas what I am doing wrong?
var xmlhttp = new XMLHttpRequest();
var url = "https://api.tfl.gov.uk/line/mode/tube/status";
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
myFunctionDisruption(xmlhttp.responseText);
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
setInterval(myFunctionDisruption, 600000);
function myFunctionDisruption(response) {
var arr = JSON.parse(response);
var i;
var out = "<table>";
for(i = 0; i < arr.length; i++) {
out += "<tr><td>" +
arr[i].lineStatuses[0].disruption.description + <!-- DOES NOT WORK -->
"</td></tr>";
}
out += "</table>";
document.getElementById("tube-disruption").innerHTML = out;
}
The below code will generate a table for you. The generic tableMaker function takes an array of an object or an array of multiple objects provided in the first argument. All objects should have same keys (properties) since these keys are used to create the table header (if the second argument is set to true) and the values are used to create each row. It will return an HTML table text. You can see the tableMaker function working with a smaller size data at here. You can also practice it with some sample and simple data you may produce.
In your case you have multiple nested objects those, I guess, need to be converted into separate tables within the corresponding cells of the main table. For that purpose i have another function tabelizer which handles this job recursively by utilizing the tableMaker function. The result of tabelizer is a complete HTML text of your master table.
Lets see
var tableMaker = (o,h) => {var keys = o.length && Object.keys(o[0]),
rowMaker = (a,t) => a.reduce((p,c,i,a) => p + (i === a.length-1 ? "<" + t + ">" + c + "</" + t + "></tr>"
: "<" + t + ">" + c + "</" + t + ">"),"<tr>"),
rows = o.reduce((r,c) => r + rowMaker(keys.reduce((v,k) => v.concat(c[k]),[]),"td"),h ? rowMaker(keys,"th") : []);
return rows.length ? "<table>" + rows + "</table>" : "";
},
data = [
{
"$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
"id": "bakerloo",
"name": "Bakerloo",
"modeName": "tube",
"disruptions": [],
"created": "2016-06-03T12:36:54.19Z",
"modified": "2016-06-03T12:36:54.19Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
"id": 0,
"statusSeverity": 10,
"statusSeverityDescription": "Good Service",
"created": "0001-01-01T00:00:00",
"validityPeriods": []
}
],
"routeSections": [],
"serviceTypes": [
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Regular",
"uri": "/Line/Route?ids=Bakerloo&serviceTypes=Regular"
}
]
},
{
"$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
"id": "central",
"name": "Central",
"modeName": "tube",
"disruptions": [],
"created": "2016-06-03T12:36:54.037Z",
"modified": "2016-06-03T12:36:54.037Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
"id": 0,
"lineId": "central",
"statusSeverity": 5,
"statusSeverityDescription": "Part Closure",
"reason": "CENTRAL LINE: Saturday 11 and Sunday 12 June, no service between White City and Ealing Broadway / West Ruislip. This is to enable track replacement work at East Acton and Ruislip Gardens. Replacement buses operate.",
"created": "0001-01-01T00:00:00",
"validityPeriods": [
{
"$type": "Tfl.Api.Presentation.Entities.ValidityPeriod, Tfl.Api.Presentation.Entities",
"fromDate": "2016-06-11T03:30:00Z",
"toDate": "2016-06-13T01:29:00Z",
"isNow": false
}
],
"disruption": {
"$type": "Tfl.Api.Presentation.Entities.Disruption, Tfl.Api.Presentation.Entities",
"category": "PlannedWork",
"categoryDescription": "PlannedWork",
"description": "CENTRAL LINE: Saturday 11 and Sunday 12 June, no service between White City and Ealing Broadway / West Ruislip. This is to enable track replacement work at East Acton and Ruislip Gardens. Replacement buses operate.",
"additionalInfo": "Replacement buses operate as follows:Service A: White City - East Acton - North Acton - West Acton - Ealing Common (for District and Piccadilly Lines) - Ealing BroadwayService B: White City - North Acton - Northolt - South Ruislip - Ruislip Gardens - West RuislipService C: White City - North Acton - Park Royal (Piccadilly Line) - Hanger Lane - Perivale - Greenford - Northolt",
"created": "2016-05-12T11:04:00Z",
"affectedRoutes": [],
"affectedStops": [],
"isBlocking": true,
"closureText": "partClosure"
}
}
],
"routeSections": [],
"serviceTypes": [
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Regular",
"uri": "/Line/Route?ids=Central&serviceTypes=Regular"
}
]
}
],
tabelizer = (a) => a.length ? tableMaker(a.map(e => Object.keys(e).reduce((p,k) => (p[k] = Array.isArray(e[k]) ? tabelizer(e[k]) : e[k],p),{})),true)
: "",
tableHTML = tabelizer(data);
document.write(tableHTML);
I used arrow functions but they might not work at Safari or IE. You might need to convert them to the conventional function notation.
You can also try the code out at repl.it where you can see the HTML text displayed through console.log.
I keep getting this SyntaxError when reading json parameters from a rest api in javascript.
Any clue what it might be?
I appreciate any help
code:
parseJSONResponse : function(inResult) {
var jsonDoc = JSON.parse(inResult);
this.fmeForm = document.forms['fmeForm'];
ar parameters = jsonDoc.serviceResponse.parameters.parameter;
for( i = 0; i < parameters.length; i++) {
if(parameters[i].type === "LOOKUP_CHOICE") {
this.createComboBox(parameters);
}
if(parameters[i].type === "LISTBOX_ENCODED") {
this.createCheckboxGroup(parameters);
}
}
},
And the json from a rest look like this:
"serviceResponse": {
"parameters": {"parameter": [
{
"optionsType": "MULTICHOICE_CONFIG",
"type": "LISTBOX_ENCODED",
"options": {"option": [
{"value": "NOR"},
{"value": "NOR_roads"}
]}
},
{
"optionsType": "SINGLECHOICE_CONFIG",
"type": "LOOKUP_CHOICE",
"options": {"option": [
{
"displayAlias": "WGS84, Lat-Long; Degrees [LL84]",
"value": "EPSG:4326"
}
]}
},
{
"name": "GEOM",
"description": "",
"defaultValue": "<lt>?xml<space>version=<quote>1.0<quote><space>encoding= <quote>US_ASCII<quote><space>standalone=<quote>no<quote><space>?<gt><lt>geometry<gt> <lt>polygon<gt><lt>line<gt><lt>coord<space>x=<quote>-124<quote><space>y=<quote>48<quote><solidus><gt><lt>coord<space>x=<quote>-124<quote><space>y=<quote>49.399999999999999<quote><solidus><gt><lt><solidus>line<gt><lt><solidus>polygon<gt><lt><solidus>geometry<gt>",
"optionsType": "SINGLECHOICE_CONFIG",
"type": "GEOMETRY",
"options": {"option": {"value": "GEOM"}}
},
Thanx!
as everyone is stating, this is absolutely not a valid JSON, whatever the case, try validating your JSON in http://jsonlint.com/, this will show you exactly where your problem might be
That is either a excerpt from the JSON response or a very broken result. One problem is that the comma at the end makes it invalid JSON...
I'm having trouble finding a solution that will help me loop through a bunch of elements and putting the chosen values into a table. I've been able to withdraw some values but the method isn't dynamic.
Here is an example:
var Table = {
"credit": {
"link": "site link",
"logoUrl": "logo url",
"message": "message"
},
"groups": [
{
"labels": [
{
"name": "Western Conference",
"type": "conference"
},
{
"name": "Central Division",
"type": "division"
}
],
"standings": [
{
"stats": [
{
"name": "gp",
"value": 20
},
{
"name": "w",
"value": 17
},
{
"name": "l",
"value": 0
},
{
"name": "gf",
"value": 64
},
{
"name": "ga",
"value": 37
},
{
"name": "gd",
"value": 27
},
{
"name": "pts",
"value": 37
}
],
"team": {
"id": 12345,
"link": "team link",
"name": "team name",
"shortName": "team"
}
},
This is the structure of the elements. So far I've used this:
document.getElementById("sGamesPlayed").innerHTML=Table.groups[0].standings[0].stats[0].value;
to withdraw values. However there are more teams, stats and divisions so I would need some kind of loop to go through the elements and put the into a dynamic table.
I would consider you to look at http://underscorejs.org/.
it provides a bunch of utility functions that could help you,
for example, _.each() helps you loop through JSON properties.
for the sample objects you've given (after completing the missing brackets at the end),
_.each(Table.groups[0].standings[0].stats, function(stats){
console.log(stats['name']+","+stats['value'])
})
gives me:
gp,20
w,17
l,0
gf,64
ga,37
gd,27
pts,37
how it works is that you provide the object you want as the first argument and the function that you give as the second argument will be called with each element of the first argument (Assuming it is a list).
I would also urge you to look at underscore templating that you can use to render your table where i put the console.log :
http://net.tutsplus.com/tutorials/javascript-ajax/getting-cozy-with-underscore-js/
http://scriptble.com/2011/01/28/underscore-js-templates/
I guess your question is about filtering the values of the array standings. In order to do that you can use the jQuery grep function (if you want to use jQuery).
For example you can write:
var arr = $.grep(Table.groups[0].standings[0].stats, function(d){return d.value>25})
Which will give
arr = [{"name": "gf","value": 64}, {"name": "ga", "value": 37},{"name": "gd", "value": 27},{"name": "pts", "value": 37}]
If this is not what you meant, can you please create a jsFiddle with a sample of what you want?
Depending on what you want to do with the results, you can go over the object using a scheme like:
var groups, standings, stats, value;
groups = Table.groups;
// Do stuff with groups
for (var i=0, iLen=groups.length; i<iLen; i++) {
standings = groups[i].standings;
// Do stuff with standings
for (var j=0, jLen=standings.length; j<jLen; j++) {
stats = standings[j];
// Do stuff with stats
for (var k=0, kLen=stats.length; k<kLen; k++) {
value = stats[k].value;
// Do stuff with value
}
}
}
Of course I have no idea what the data is for, what the overall structure is or how you want to present it. But if you have deeply nested data, all you can do is dig into it. You might be able to write a recursive function, but it might also become very difficult to maintain if the data structure is complex.
I have a JSON object from the CrunchBase API giving me a bunch of info from a given company. Right now I am trying to go through the JSON object and create a list of their investors. The investors can fall into one of three categories, "company", "financial_org", or "person". All three types will be appended to the same list, finalInvestorList
The script runs without error, but only produces a list of investors from the first listed round. I have logged everything I think might help. the logs are in same-line comments.
Basically my problem is that it is only looping through one time, and therefor only adding the investors from the first round. Any help would be greatly appreciated. Let me know if you need more info!
var investorList = function(data, num) {
var fundingRounds = data["funding_rounds"];
var finalInvestorList = []
console.log(fundingRounds.length) // 3
for (i=0; i < fundingRounds.length; i++) {
var investments = data["funding_rounds"][i]["investments"];
console.log(data["funding_rounds"][1]["investments"]); //correctly logs the index 1 round for spling (2nd round)
var round = data["funding_rounds"][i];
console.log('round' + i); //only logs round0, never loops around for round1, round2
for (i=0; i < investments.length; i++) {
var angelObject = round["investments"][i]["person"];
if (angelObject != null) {
console.log("angel fired"); //fires for "Mitch Blumenfeld"
var angel = angelObject["first_name"] + " " + angelObject["last_name"];
finalInvestorList[i] = angel;
}
var financialOrgObject = round["investments"][i]["financial_org"];
if (financialOrgObject != null) {
console.log("financial_org fired"); //fires for "Bucknell Venture Plan Competition"
console.log(financialOrgObject['name']); //Bucknell VPC
var financialOrg = financialOrgObject["name"]
finalInvestorList[i] = financialOrg
}
var companyObject = round['investments'][i]["company"];
if (companyObject != null) {
console.log('company fired'); //i haven't bothered with this yet.. just logging it so ill know if its firing
}
}
}
console.log(finalInvestorList); //["Bucknell Venture Plan Competition", "Mitch Blumenfeld"]
}
The JSON object represented by the data is as follows
I have shortened it with just the bit needed. The object inside the JSON response data is represented by data["funding_rounds"]
this data was retrieved using the crunch API and can be found in full form at http://api.crunchbase.com/v/1/company/spling.js
"funding_rounds":
[{"round_code": "seed",
"source_url": "",
"source_description": "",
"raised_amount": 50000.0,
"raised_currency_code": "USD",
"funded_year": 2011,
"funded_month": 2,
"funded_day": 1,
"investments":
[{"company": null,
"financial_org":
{"name": "Bucknell Venture Plan Competition",
"permalink": "bucknell-venture-plan-competition",
"image": null},
"person": null},
{"company": null,
"financial_org": null,
"person":
{"first_name": "Mitch",
"last_name": "Blumenfeld",
"permalink": "mitch-blumenfeld",
"image": null}}]},
{"round_code": "seed",
"source_url": "http://techcrunch.com/2011/09/08/dreamit-ventures-launches-its-fall-2011-philadelphia-class/",
"source_description": "",
"raised_amount": 25000.0,
"raised_currency_code": "USD",
"funded_year": 2011,
"funded_month": 9,
"funded_day": 1,
"investments":
[{"company": null,
"financial_org":
{"name": "DreamIt Ventures",
"permalink": "dreamit-ventures",
"image":
{"available_sizes":
[[[150,
57],
"assets/images/resized/0002/7773/27773v5-max-150x150.jpg"],
[[250,
96],
"assets/images/resized/0002/7773/27773v5-max-250x250.jpg"],
[[251,
97],
"assets/images/resized/0002/7773/27773v5-max-450x450.jpg"]],
"attribution": null}},
"person": null}]},
{"round_code": "a",
"source_url": "http://techcrunch.com/2011/12/05/new-content-sharing-network-spling-launches-announces-400k-series-a/",
"source_description": "New Content Sharing Network Spling Launches, Announces $400K Series A",
"raised_amount": 400000.0,
"raised_currency_code": "USD",
"funded_year": 2011,
"funded_month": 12,
"funded_day": 5,
"investments":
[{"company": null,
"financial_org":
{"name": "Deep Fork Capital",
"permalink": "deep-fork-capital-2",
"image":
{"available_sizes":
[[[150,
20],
"assets/images/resized/0008/0167/80167v1-max-150x150.png"],
[[250,
34],
"assets/images/resized/0008/0167/80167v1-max-250x250.png"],
[[450,
62],
"assets/images/resized/0008/0167/80167v1-max-450x450.png"]],
"attribution": null}},
"person": null},
{"company": null,
"financial_org": null,
"person":
{"first_name": "John",
"last_name": "Ason",
"permalink": "john-ason",
"image": null}},
{"company": null,
"financial_org": null,
"person":
{"first_name": "Mitchell",
"last_name": "Blumenfeld",
"permalink": "mitchell-blumenfeld",
"image": null}},
{"company": null,
"financial_org": null,
"person":
{"first_name": "Gianni",
"last_name": "Martire",
"permalink": "gianni-martire",
"image":
{"available_sizes":
[[[138,
150],
"assets/images/resized/0006/3720/63720v4-max-150x150.jpg"],
[[230,
250],
"assets/images/resized/0006/3720/63720v4-max-250x250.jpg"],
[[414,
450],
"assets/images/resized/0006/3720/63720v4-max-450x450.jpg"]],
"attribution": ""}}}]}]
Thanks for the help!
You are using the same variable name for the counter in each loop, so when the inner loop completes and the outer loop gets to its second iteration, i is investments.length, not 1. Use different variable names for each loop:
for (var roundIdx = 0; roundIdx < fundingRounds.length; roundIdx++) {
...
for (var invIdx = 0; invIdx < investments.length; invIdx++) {
...
Also, don't populate your array using array[i] = value notation, just use array.push(value). You don't need to keep track of the indexes.
But, I'd recommend iterating your arrays using Array.forEach() and using dot syntax instead of square bracket notation:
function investorList(data, num) {
var finalInvestorList = [];
data.funding_rounds.forEach(function (round, i) {
round.investments.forEach(function (investment, i) {
if (investment.person) {
finalInvestorList.push(investment.person.first_name + " " +
investment.person.last_name);
}
else if (investment.financial_org) {
finalInvestorList.push(investment.financial_org.name)
}
else if (investment.company) {
finalInvestorList.push(investment.company.name)
}
}
}
}
For older browsers that don't natively support Array.forEach(), use the implementation here to shim the browser.