Need help accessing JSON array object - javascript

Objective: This code collects an array JSONAPIS and passes the APIS into a $.each() loop. Then JSON data field are evaluated in if statements and used to calculate precip. How do I access the JSONAPIS from the obj.
Main issue: obj.daily.data.length is undefined on the daily array member. The obj should contain one of the API calls and the JSON dataset to use. Instead it contains keyword like abort, always, promise which I am not familiar how to use. What would access the result JSON object property?
var listAPIs = "";
var darkForecastAPI = [];
var result = [];
var JSONAPIS = [];
$.each(numDaysAPITimes, function(a, time) {
var darkForecastAPI = /*"http://api.wunderground.com/api/" + currentAPIKey + "/history_" + time + "/q/" + state + "/" + city +".json?callback=?"; */
"http://api.forecast.io/forecast/" + currentAPIKey + "/" + city + time + "?callback=?";
//https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME
JSONAPIS.push($.getJSON(darkForecastAPI, {
tags: "WxAPI[" + i + "]", //Is this tag the name of each JSON page? I tried to index it incase this is how to refer to the JSON formatted code from the APIs.
tagmode: "any",
format: "json"
}));
});
$.when.apply($, JSONAPIS).done(function(result) { /*no log simply an array */
var eachPrecipSum = 0.0;
var totalPrecipSinceDate = 0.0;
alert(result);
$.each(result, function(d, obj) {
console.log(obj);
for (var c = 0; c <= obj.daily.data.length - 1; c++) {
if (obj.daily.data[c].precipIntensity >= 0.0000 && obj.daily.data[c].precipType == "rain") /*Number(result.history.dailysummary.precipm, result.history.dailysummary.rain*/ {
eachPrecipSum = result[d].daily.data[c].precipIntensity;
totalPrecipSinceDate = eachPrecipSum + totalPrecipSinceDate; ///Write mean precip
alert(Math.round(eachPrecipSum * 10000) / 10000);
$("body").append("p").text("There has been as least a total of " + Math.round(totalPrecipSinceDate * 10000) / 10000 + " inches per hour of rain at the location in the last " + userDataDatePick + " days")
} else if (obj.daily.data[c].precipIntensity >= 0.0000 && obj.daily.data[c].precipType != "rain") {
alert("There is was no rain on ____" /*+ result.history.dailysummary.mon + "/" + result.history.dailysummary.mday + "/" + result.history.dailysummary.year*/ );
}
}
});
});
numDaysAPITimes = 0;
}

$.when doesn't take array as input
Since you are passing an array that isn't itself a promise it is likely firing immediately and therefore ahead of all the ajax calls completing
Need to change to
$.when.apply(null, JSONAPIS).done...

Related

Get values from JSON Parse Object within Object; Dynamic Keys (JavaScript)

While I am able to properly parse this payload if I specify the keys via bracket notation, how could you parse it dynamically?
{
"name": "Demo User",
"birthday": "January 1st",
"hobbies":
{
"morning": "coding",
"afternoon": "kewl kids stuff",
"nighttime": "random"
},
"date": "June 25th"
}
What I mean by that is hobbies might not be there, or another field such as 'favorite foods' might exist, which could be an array, or an object.
This scenario has caused me a lot of frustration over the last couple months, and I'm trying to see if anyone can explain to me how to successfully parse it, dynamically.
I found a recursive 'walk' approach that is no longer erroring, but it's returning the first thing it comes across for every iteration.
var data = require("./demo.json");
//data = JSON.stringify(data);
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
walk(val);
}
}
}
walk(data);
Basic on what I found, data is being imported as JSON, and if I stringify it, it becomes a string (obviously).
Default
{ name: 'Demo User',
birthday: 'January 1st',
hobbies:
{ morning: 'coding',
afternoon: 'kewl kids stuff',
nighttime: 'random' },
date: 'June 25th' }
Stringify
{"name":"Demo User","birthday":"January 1st","hobbies":{"morning":"coding","afternoon":"kewl kids stuff","nighttime":"random"},"date":"June 25th"}
Both are similar, but the only difference on output is it spamming D X times (Being the first value, I'm thinking?) or spamming { X times (Being the first value of the string?
I've tried a much more basic approach of
var data = require("./demo.json");
for (var key in data){
console.log(key + ':' + data[key]);
}
Which works fine, but as expected, hobbies is returning [object Object] since I am not iterating through it. I could go through hobbies, but again - I don't know if it will exist.
Welcome any input - Generic question, but a process that has caused me a lot of frustration on different projects over last few months.
UPDATE
My vagueness is causing, rightfully-so, confusion.
Let's say my objective is to turn this JSON Payload into a CSV. I need every key for the headers, and every value to be a row under said header.
My issue is, as I iterate through it, I end up with the highest-level objects correctly converted. Then I end up with an object Object column with no data.
For this exact example, let's say my goal is to convert the JSON into
name, birthday, hobbies/morning, hobbies/afternoon, hobbies/nighttime, data
Demo User, January 1st, coding, kewl kids stuff, random, June 25th
Update # 2
Additional array variant.
I would expect
{
...
"hobbies":
{
"morning": "coding",
"afternoon": "kewl kids stuff",
"nighttime": "random"
},
...
}
To output
hobbies/morning, hobbies/afternoon, hobbies/nighttimes
I would expect
{
...
"hobbies": ["coding", "kewl kids stuff", "coding"]
...
}
To output one column
hobbies with quote-enclosed items
"coding, kewl kids stuff, coding"
You can check the type of each value and decide what you want to do,
var data = require("./demo.json");
walk(obj){
for (var key in data){
if(type(data[key]) === "string"){
console.log(key + ':' + data[key]);
}
else if(Array.isArray(data[key])){
//its an array
}
else if(type(data[key]) === "object"){
//its an object
walk(data[key])
}
}
}
The reason your walk function is spamming you with D or { is because it goes on infinite loop when it encountners an string,
function walk(obj) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
console.log(val);
//here you need to check if it is an array or object, only then you should call walk
//calling walk on string would send it on a infinite loop
if(typeof(val) === "object"){
walk(val);
}
}
}
}
Try using this function ( Snippet at the end of the answer )
/**
*
* #param {object} input
* #returns {Array<string>}
*/
function translateObject(input) {
if (typeof input === "object" && input !== null) {
if (input instanceof Array) {
var result = '"';
for (var index in input) {
if (index) result += ", ";
result += input[index];
}
return [result + '"'];
} else {
var data = "", result = "";
for (var key in input) {
if (key.includes(",")) {
throw new Error("Key cannot have a comma");
}
var val = translateObject(input[key]);
if (val.length === 2) {
var titles = val[0].split(", ");
var textIndex = 0;
var size = 0;
for (var index in titles) {
var title = titles[index];
var titleVal = val[1].substring(textIndex, textIndex + title.length);
if (result) { result += ", "; data += ", "; }
textIndex += title.length + 2;
title = key + "/" + title;
size = Math.max(title.length, titleVal.length);
result += title + " ".repeat(size - title.length);
data += titleVal + " ".repeat(size - titleVal.length);
}
} else if (val.length === 1) {
size = Math.max(val[0].length, key.length);
if (result) { result += ", "; data += ", "; }
result += key + " ".repeat(size - key.length);
data += val[0] + " ".repeat(size - val[0].length);
}
}
return [result, data];
}
}
return [input];
}
Here is a working example:
var object = {
"a": "1",
"b": "2",
"c":
{
"e": "3",
"f": "4",
"g": "5"
},
"d": "6"
};
function translateObject(input) {
if (typeof input === "object" && input !== null) {
if (input instanceof Array) {
var result = '"';
for (var index in input) {
if (index) result += ", ";
result += input[index];
}
return [result + '"'];
} else {
var data = "", result = "";
for (var key in input) {
if (key.includes(",")) {
throw new Error("Key cannot have a comma");
}
var val = translateObject(input[key]);
if (val.length === 2) {
var titles = val[0].split(", ");
var textIndex = 0;
var size = 0;
for (var index in titles) {
var title = titles[index];
var titleVal = val[1].substring(textIndex, textIndex + title.length);
if (result) { result += ", "; data += ", "; }
textIndex += title.length + 2;
title = key + "/" + title;
size = Math.max(title.length, titleVal.length);
result += title + " ".repeat(size - title.length);
data += titleVal + " ".repeat(size - titleVal.length);
}
} else if (val.length === 1) {
size = Math.max(val[0].length, key.length);
if (result) { result += ", "; data += ", "; }
result += key + " ".repeat(size - key.length);
data += val[0] + " ".repeat(size - val[0].length);
}
}
return [result, data];
}
}
return [input];
}
function objectToCsv(object) {
var result = translateObject(object);
return result[0] + "\n" + result[1];
}
var csv = objectToCsv(object);
document.querySelector("#console").innerText = csv;
console.log(csv);
#console {
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
white-space: pre;
}
span {
color: darkgrey;
}
<div id="console"></div>
<span>Names were minified to fit result in one line so that it is easier to read</span>
<span>Use this font family if you want all characters to have the same width</span>
Maybe what you are describing is a case where one or more attributes of the object you are expecting is not present, or has no content (or members, if it is an array), and how to build your code on that basis.
It can't be entirely random, otherwise you wouldn't be talking about csv style output. So I will assume that your object contents are mostly there but occasionally you'll find something missing.
If it were me I would pre-process the objects loaded when you use JSON.parse() or whatever equivalent you use to convert the string to javascript objects. I would use something like jquery's $.extend to merge a perfectly formed object into my data, and array merging where the target is an array attribute. This would give me a consistent data model to code against.
In summary - you have to make the data the way you want it to be in order to be able to work with it without surprises.

Returning a String instead of looping print statement

I'm new to Javascript and I'm curious as to how to store values in a string and then return it. In the example below 2 numbers are picked, for example 2 and 8, and the program should return 2x1 =2, 2x2=4,..... all the way up to 2x8 =16. This can obviously be done by constantly looping a print statement as I have done, but how would I be able to store all the values in a String and then return the string.
function showMultiples (num, numMultiples)
{
for (i = 1; i < numMultiples; i++)
{
var result = num*i;
console.log(num + " x " + i + " = " + result+ "\n");
}
}
console.log('showMultiples(2,8) returns: ' + showMultiples(2,8));
console.log('showMultiples(3,2) returns: ' + showMultiples(3,2));
console.log('showMultiples(5,4) returns: ' + showMultiples(5,4));
function showMultiples(num, numMultiples) {
// the accumulator (should be initialized to empty string)
var str = "";
for (i = 1; i < numMultiples; i++) {
var result = num * i;
// use += to append to str instead of overriding it
str += num + " x " + i + " = " + result + "\n";
}
// return the result str
return str;
}
var mulOf5 = showMultiples(5, 10);
console.log("multiples of 5 are:\n" + mulOf5);
The operator += add the a value (right operand) to the previous value of the left operand and stores the result in the later. So these two lines are the same:
str = str + someValue;
str += someValue;
You could just use string concatenation:
var finalResult = ""
...in your loop...
finalResult += num + " x " + i + " = " + result+ "\n"
Often you can also just collect the results in an array and use join to append them.
var lines = [];
... in your loop:
lines.push(num + " x " + i + " = " + result);
... afterwards
console.log(lines.join("\n"));
In case you wanted to use ES6 syntax using backticks for a template string, you can use the below. This is a little more readable and is exactly where it is useful (so long as you can use ES6 wherever you're using JavaScript).
function showMultiples(num, numMultiples){
let result = '';
for(let i = 1; i < numMultiples; i++){
result += `${num} x ${i} = ${i * num}\n`;
};
return result;
}
console.log(showMultiples(2,8));

Sorting csv according to particular column

Hi I am exporting data to csv file using javascript. I need to sort the data according to specific column index.
Code:
$.each(exportArray, function (index, value) {
csvData.push(x[index] + "," + y[index] + "," + d[index] + "," + z[index] + ","+ a[index] + "," + e[index] + "," + b[index] + "," + c[index]);
});
csvData.sort();
csvData = 'data:application/csv;charset=utf-8,' + encodeURIComponent(output);
$(this)
.attr({
'download': filename,
'href': csvData,
'target': '_blank'
});
E.g.: csvData.sort() is sorting data acoording to first column as default. I want to have it sorted according to the third column i.e.. by d[index].
I tried this csvData.sort(sort_by(d[index])); and it is not working.
I have one more issue . Since I am exporting the data to csv. Now in d[index] from the server if I have three values like d[index]="cat,dog,bat" . It is getting displayed in 3 adjacent columns. I want it in the same column. Can I change the delimiter to something else from comma.
Code:
csvData.push(x[index] + "," + y[index] + "," + d[index] + "," + z[index] + ","+ a[index] + "," + e[index] + "," + b[index] + "," + c[index]);
You need to sort the data before you write it to the CSV. This is more easily done if the data is in an array of objects instead of spread out in one array per property. To achive this you can either just put the data in objects when it is created or read, or you can convert it with a snippet like this:
var count = x.length;
var objects = new Array(count);
for(i=0; i++; i<count) {
objects[i] = {
x: x[i],
y: y[i],
d: d[i],
z: z[i],
a: a[i],
e: e[i],
b: b[i],
c: c[i],
};
}
Then you can easily sort the data, for example by the x property, with this code:
function compare(a, b) {
if (a.x < b.x) return -1;
if (a.x > b.x) return 1;
return 0;
}
objects.sort(compare);
Then you will get the arrays sorted, and can access individual properties with for instance objects[i].x.
You have to write a custom compare function
// The following function is used to sort on the basis of the third element
function compareByThird(a, b) {
var aArr = a.split(',');
var bArr = b.split(',');
return aArr[2] - bArr[2];
}
csvData.sort(compareByThird);
It works if the third column is a numeric value.
If not (for example for strings) it necessary to use the following code:
// The following function is used to sort on the basis of the third element
function compareByThird(a, b) {
var aArr = a.split(',');
var bArr = b.split(',');
if (aArr[2] < bArr[2]) {
return -1;
} else if (aArr[2] > bArr[2] {
return 1;
} else {
return 0;
}
}

How can I group data returned in JSON format and return it?

I have a JSON string of the form:
[
{"ID":153,"CircuitID":53,"StartTime":"2014-11-12 12:45:00","EventFormatID":224,"TotalPlaces":8,"BookedPlaces":0,"ProvisionalPlaces":0},
{"ID":161,"CircuitID":53,"StartTime":"2014-11-12 17:15:00","EventFormatID":224,"TotalPlaces":0,"BookedPlaces":0,"ProvisionalPlaces":0},
{"ID":734,"CircuitID":53,"StartTime":"2014-11-12 18:30:00","EventFormatID":231,"TotalPlaces":14,"BookedPlaces":0,"ProvisionalPlaces":0}
]
In place of Event Format ID and Circuit ID I will be returning the names
What I need to do is group the results by Event Format ID and return the results in the following format:
Event 224 : 12:45 (8 places available), 17:15 (0 places available)
Event 231 : 18:30 (14 places available)
I can't seem to figure out how to loop through the data, group it by Event Format ID to present it in the required format!
Thanks
Can you use any additional libraries? I'd use lo-dash which would make this relatively simple:
var grouped = _.groupBy(data, "EventFormatID");
_(grouped).forEach(function (group, key) {
console.log("Event:" + key);
_(group).forEach(function (course) {
console.log(course.StartTime + " (" + course.TotalPlaces + " places available)");
});
});
Obviously the example above logs to the console but it'd be fairly simple to change to build up whatever string or object you need.
This is easier with lodash/underscore, or even with ES5 array and object methods, but since you asked about pure JS:
var data = {}, results = [], i, j, id, time, obj, evts; // obj is your object above
for (i=0; i<obj.length; i++) {
id = obj[i].EventFormatID;
time = obj[i].StartTime; // you can simplify to get just the time, not the datetime, if you prefer
data[id] = data[id] || [];
data[id].push({"time":time,"places":obj[i].TotalPlaces});
}
// now you have a proper data structure, just print it out
for (i in data) {
if (data.hasOwnProperty(i)) {
// just show the output properly formatted
evts = [];
for (j=0;i<data[i].length;j++) {
evts.push(data[i][j].time+" ("+data[i][j].places+" places available)");
}
results.push("Event "+i+" : "+evts.join(","));
}
}
ES5 makes this so much easier
var data = {}, results = [], obj; // obj is your object above
obj.forEach(function(val,i) {
data[val.EventFormatID] = data[val.EventFormatID] || [];
data[val.EventFormatID].push({"time":val.StartTime,"places":val.TotalPlaces});
});
// now you have a proper data structure, just print it out
Object.keys(data).forEach(function(key) {
var value = data[key], evts = [];
value.forEach(function(elm) {
evts.push(elm.time+" ("+elm.places+" places available)");
});
results.push("Event "+key+" : "+evts.join(","));
});
And lodash is even easier.
Please take a look that:
http://jsfiddle.net/m260n5ud/
html
<div id="contentDiv"></div>
js
function tidyUp(jsonArray) {
var myObject = {};
for (i = 0; i < jsonArray.length; i++) {
var key = jsonArray[i]['EventFormatID'];
var time = jsonArray[i]['StartTime'].replace(' ', ':').split(/[- :]/);
time = time[3] + ":" + time[4];
var totalPlace = jsonArray[i]['TotalPlaces'];
if (myObject[key] == null) {
myObject[key] = "Event : " + key + " : " + time + " ( " + totalPlace + " places available)";
} else {
myObject[key] += ", " + time + " ( " + totalPlace + " places available)";
}
}
console.log(myObject);
for (var k in myObject) {
document.getElementById('contentDiv').innerHTML += myObject[k] + "<br/>";
}
}

How to create an array of variables from an array in Javascript

I have a variable called "information" which creates a multi-dimensional array. For each row in the array, I want to return a variable whose name is the first value in the array. In other words, given the 'information' array below, I'd want the following output:
var lunalovegood = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Luna Lovegood is a Ravenclaw!;
var dracomalfoy = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Draco Malfoy is a Slythering!;;
var hermionegranger = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Hermione Granger is a Gryffindor!;;
In other words, I want to be able to work with each of the elements in the 'information' array to create some markup. I already know how to get the information I need given the information array, but as you can see below I'd have to declare separate variables for each of the names.
for (var i = 0; i < information.length; i++) {
var htmlString = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i] [1] + '!'; //Luna Lovegood is a Ravenclaw!
$('div').html(htmlString);
} //end for loop
var information = [
['lunalovegood', 'Ravenclaw', 'Luna', 'Lovegood', '(chaser)', 'lovegood.jpg', 4]
['dracomalfoy', 'Slytherin', 'Draco', 'Malfoy', '(seeker)', 'malfoy.jpg', 2],
['hermionegranger', 'Gryffindor', 'Hermione', 'Granger', '(none)', 'granger.jpg', 3],
];
The javascript below creates three variables called 'lunalovegood', 'dracomalfoy', and 'hermionegrange', but it's the long way of creating variables. How do I create these variables, one for each row in the array, by looping through the 0th indexed element in the 'information' array?
var myVariables = {}
,varNames = ["lunalovegood","dracomalfoy","hermionegranger"];
for (var i=0;i<varNames.length;i+=1){
myVariables[varNames[i]] = 0;
console.log(lunalovegood);
}
Your current approach just needs a most minor tweak to not require the second array.
var students = {}, i;
for (i = 0; i < information.length; ++i)
students[information[i][0]] = information[i][2] + ' ' + information[i][3] + ' is a ' + information[i][1] + '!';
Now the key is set by taking the first item of the Array. You would then do the following for your text,
students['lunalovegood']; // "Luna Lovegood is a Ravenclaw!"
You're also missing a , in your information literal.
This should help you:
Every variable in the global scope can be accessed as a string property of the window object
var myvariable = 4;
alert(window["myvariable"]); // will alert 4
window["newvariable"] = 6;
alert(newvariable); // will alert 6
I agree with Bergi. Variables should represent a fixed finite set of members defined by code; data (as in the contents of a list) should generally not introduce new variables.
As such, here is the approach I would recommend (note that I've added a bit more than the "minimum required"; good luck!):
// Using a function makes it easy to change details and avoid leaking
// variables accidentally.
function loadWizards(information) {
var wizards = [];
for (var i = 0; i < information.length; i++) {
var info = information[i];
var name = info[0];
// Mapping to named properties means we can forget about indices!
wizards[name] = { // <- use Name to map to our Wizard object
house: info[1],
// ..
image: info[7]
};
}
return wizards;
}
// I have no idea if they are wizards, but give variables useful names.
// 'information' is too generic.
var wizards = loadWizards(information);
// Then later on, use it as:
alert("Hello " + wizards['hermionegranger'].name + "!")
// ^-- property access by Name
var formattedInfo = {};
$.each(information, function (i, v) {
formattedInfo[v[0]] = v[2] + ' ' + v[3] + ' is a ' + v[1];
});
there is a missing comma at the end of the 1st line of your definition of information.
BTW, I like Harry Potter very much.

Categories

Resources