How to have a foreach inside a foreach in JQuery? - javascript

Hey guys! First of all, I know this kind of stuff is quite shitty, but that's the way I visualize achieving my goal.
Here is the deal: I have a JSon object that contains an array, but unfortunately, I have to iterate through the list of one JSon object, and then iterate throught that array I mencioned before. The JSon object look as follows (it is in portuguese, but you guys can understand it):
{"produtos":[{"Key":"DERIVADOS DE FERRO","Value":217816909},{"Key":"MADEIRAS,
SUAS MANUFATURAS E MOBILIÁRIO MÉDICO
CIRÚRGICO","Value":117812290},{"Key":"CELULOSE,
PAPEL E SUAS
OBRAS","Value":100086937},{"Key":"CARNE
SUÍNA","Value":81738783},{"Key":"CARNE
BOVINA","Value":74894768},{"Key":"CARNE DE
AVES","Value":65292433},{"Key":"Outros","Value":444520811}],"ano":2005,"tipo":1}
Produtos is the array I mentioned, and I have two other values that are important: ano (year) and tipo (type). My code look as follows:
jQuery.getJSON("/webportos/Porto/GraficoComercio?id=" + iddoporto.className + "&ano=2005", null, function (items) {
jQuery.each(items, function (itemNo, item) {
jQuery.each(item.produtos, function (dicNo, dic) {
options.series.push({ name: dic.Key, data: dic.Value});
options.title.text = "Comércio - "+item.ano;
options.yAxis.title.text = "Exportação";
options.tooltip.formatter = function () {
return '<b><u>' + this.series.name + '</u></b><br/><b>' + this.x + '</b>: ' + this.y + ' ';
}
});
});
chart = new Highcharts.Chart(options);
});
This piece of code makes Chrome javascript console to display:
ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js:32 Uncaught TypeError: Cannot read property 'length' of undefined
Can you guys help me out?

Is this because you're trying to use .each() on a non array?
jQuery.each(items, function (itemNo, item) {
From my understanding, jQuery.each(object, callback, args) is only for use on arrays. The first thing it does is make a call to object.length. So unless you have
[{"produtos":[{"Key":"DERIVADOS DE FERRO","Value":217816909},{"Key":"MADEIRAS, SUAS
MANUFATURAS E MOBILIÁRIO MÉDICO CIRÚRGICO","Value":117812290},{"Key":"CELULOSE, PAPEL E SUAS
OBRAS","Value":100086937},{"Key":"CARNE SUÍNA","Value":81738783},{"Key":"CARNE
BOVINA","Value":74894768},{"Key":"CARNE DE AVES","Value":65292433},
{"Key":"Outros","Value":444520811}],"ano":2005,"tipo":1}]
you're gonna get that error.
Additionally, you may take Diodeus's suggestion into consideration. jQuery.each is quite a bit less performant than just using a for to loop through your data.

You don't need jQuery to iterate through JSON data. Use plain old Javascript.
See this question: Display JSON/YAML hierarchy as a tree in HTML?
function renderJSON(obj) {
var keys = []
var retValue = ""
for (var key in obj) {
if(typeof obj[key] == 'object') {
retValue += "<div class='tree'>" + key
retValue += renderJSON(obj[key])
retValue += "</div>"
}
else {
retValue += "<div class='tree'>" + key + " = " + obj[key] + "</div>"
}
keys.push(key)
}
return retValue
}

Related

Build dynamic table from JSON

I've been stuck trying to build this dynamic table for the past couple of days. I've built it out several different ways and have finally gotten it to a point where I have the correct output, however the work I've done is manual. I am hoping someone could help me make this be more dynamic.
here's an example of my JSON (super simplified)
var obj1 = {
"Summary" :
[
{
"ID" : "1234",
"Name" : "John",
"Status" : "Green",
},
{
"ID" : "5678",
"Name" : "Mike",
"Status" : "Green",
},
{
"ID" : "9012",
"Name" : "Tom",
"Status" : "Red",
},
{
"ID" : "3456",
"Name" : "Chris",
"Status" : "Red",
},
{
"ID" : "2445",
"Name" : "Pat",
"Status" : "Green",
},
{
"ID" : "6543",
"Name" : "James",
"Status" : "Red",
}
]
};
I need the output to look something like this (which it is), however I may have more than 6 objects in my array, so I need to iterate through this rather than build it out by hand.
1234 5678 9012 3456 2445 6543
John Mike Tom Chris Pat James
Green Green Red Red Green Green
Here's my code thus far. Any help would be much appreciated.
for (j in obj1.Summary[0]) {
document.write('<tr><td class="' + j +'">' + j + '</td><td class="' + j +'">' + obj1.Summary[0][j] + '</td><td class="' + j +'">' + obj1.Summary[1][j] + '</td><td class="' + j +'">' + obj1.Summary[2][j] + '</td><td class="' + j +'">' + obj1.Summary[3][j] + '</td><td class="' + j +'">' + obj1.Summary[4][j] + '</td><td class="' + j +'">' + obj1.Summary[5][j] + '</td></tr>');
}
I recently came up with an interesting pattern for creating tables from database output dynamically, while holding references to the relevant created elements and the values used to create them.
This method came in handy for me because I created input elements for each table cell, then utilized the structure created to check the original values against their actual values and generate an sql update query based on the changed fields.
I realize that it may be overkill for this specific situation, but it does aid in readability and maintainability so I'm posting it here in case it could possibly help someone else in the future.
var responseText = {"Summary":[{"ID":"1234","Name":"John","Status":"Green",}, {"ID":"5678","Name":"Mike","Status":"Green",}, {"ID":"9012","Name":"Tom","Status":"Red",}, {"ID":"3456","Name":"Chris","Status":"Red",}, {"ID":"2445","Name":"Pat","Status":"Green",}, {"ID":"6543","Name":"James","Status":"Red",}]};
var list = new List(responseText.Summary);
document.body.appendChild(list.node);
function List(data) {
if(!(this instanceof List)) return false;
var list = this.node = document.createElement('div');
list.setAttribute('class','list');
var items = this.items = {};
for(var i in data) {
items[i] = new ListItem(data[i])
list.appendChild(items[i].node);
}
}
function ListItem(data) {
if(!(this instanceof ListItem)) return false;
var item = this.node = document.createElement('div');
item.setAttribute('class','item');
var lines = this.lines = {};
for(var i in data) {
lines[i] = new ListItemLine(i, data[i])
item.appendChild(lines[i].node);
}
}
function ListItemLine(name, value) {
if(!(this instanceof ListItemLine)) return false;
var line = this.node = document.createElement('div');
this.name = name;
this.value = value;
line.setAttribute('class','line ' + name);
if(name !== 'ID')
line.setAttribute('contenteditable', true);
line.appendChild(document.createTextNode(value));
}
.item {
display: inline-block;
padding: 5px;
text-align: center;
}
Then I used something similar to this inside the List class,
list.onkeydown = function(e) {
if(e.which !== 13) return true;
e.preventDefault();
var changes = [];
for(var i in items) (function(item, data){
for(var i in data.lines) (function(line){
var value = line.value,
actual = line.node.textContent;
if(value !== actual) changes.push({
id: data.lines['ID'].value,
name: line.name,
value: actual
});
})(data.lines[i]);
})(i, items[i]);
console.log(encodeURIComponent(JSON.stringify(changes)));
}
Where instead of using console.log, I send the data via ajax to a receiver page that generates update sql and returns the result of the query. Of course there are many methods of doing the last part of this, this is the one that was the most useful for me.
Make a variable and put the text in there. That way you can build it using nested loops then insert it in the document. I just did something similar in PHP that can take a db table as nested arrays and generate a table from it.
var table = "<table>";
//we loop over the attributes since you want that format
for (userAttribute in obj1.Summary[0]) {
//these are your headers/titles
table += '<tr><th>' + userAttribute + '</th>';
//and here we build a row getting the attribute from each user
for (userIndex in obj1.Summary) {
var user = obj1.Summary[userIndex];
table += '<td>' + user[userAttribute] + '</td>';
}
table += '</tr>'; //close that row and move on to the next attribute
}
//close out the table
table += '</table>';
document.write(table);
When you want to repeat logic, you should use a loop.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
for (var i=0; i < obj.Summary.length; i++) {
var object = obj.Summary[i]
// write to document
}
You should either transpose your data, or change your interface as well.
1234 John Green
5678 Mike Green
You may find a rendering library useful as well, to avoid messing with string concatenation.
https://github.com/leonidas/transparency
Edit
No problem, still use loops. Build the rows in the loop and concatenate them together. See the mdn array docs especially forEach and join.
// specify keys and init rows
var keys = [ "ID" ]
var rows = {}
keys.forEach(function (key) {
rows[key] = []
})
// ok now we have rows
console.log(rows)
// add table cells to rows
summaryObjects.forEach(function (object) {
for (var key in object) {
var cell = "<td>" + object[key] + "</td>"
rows[key].push(cell)
}
})
// now we have cells in the rows
console.log(rows)
// put together the table
keys.forEach(function (key) {
document.write("<tr>" + rows[key].join('') + "</tr>")
})
That's what i mean by transpose above, like a matrix transpose in linear algebra. Your data looks like this:
[
{ key: value, key: value }
{ key: value, key: value }
]
And you want
{
key: [ value, value ],
key: [ value, value ]
}
I would suggest using template such as Handlebarsjs to do this. This way, you keep the html separated from JavaScript and you don't have to add so many '+'.
For example, you can embed this on your html.
<script id="template" type="text/x-handlebars-template">
{{#each Summary}}
<tr><td>{{this.ID}}</td>
<td>{{this.Name}}</td>
<td>{{this.Status}}</td>
</tr>
{{/each}}
</script>
I think I messed up the tags but you get the idea.
Then on the script file, you can compile the template and get the data. More info on http://handlebarsjs.com/
var PROJECT_METHOD ={
handlerData:function(resJSON){
var templateSource = $("#template").html(),
template = Handlebars.compile(templateSource),
projectHTML = template(resJSON),
parser = new DOMParser(),
// the result
doc = parser.parseFromString(projectHTML, "text/html");
document.write(doc);
},
loadProjectData : function(){
$.ajax({
url:"data.json",
method:'get',
success:this.handlerData
});
}
};
PROJECT_METHOD.loadProjectData();
Hope it helps.

Calling Javascript function from along with json

Hi all i am calling a javascript function on WebView in android. I am sending a JSON data which will pass value to html.
My JSON data is in following format i have checked using online tools it is valid.
{"Results":{"Number of Tests":"2","Latency(avg)":"17","Failure":"0%","Latitude":"12° 55' 35.5872'' N","Longitude":"77° 36' 4.16916'' E","Latency(max)":"18","Latency(min)":"17"},"TestStaus":"Passed","Test":"Game Test"}
I am using following code to display parsed result in html using jquery.
var jsonObject = JSON.stringify(vk);
document.write(jsonObject);
$.each($.parseJSON(jsonObject), function(k, v)
{
document.write("<tr><td>" + k + "</td><td>" + v + "</td></tr>");
});
It is giving me output in following manner
Parameter Value
Results [object Object]
TestStatus Passed
Test Game Test
Please help how to read all results. Why it is reading object object.
Just use recursion. You need to be able to handle multidimensional objects. Also, I usually use jquery for DOM or AJAX only. For something like this, you might not need it.
Your Json
var vk = {"Results":{
"Number of Tests":"2",
"Latency(avg)":"17",
"Failure":"0%",
"Latitude":"12° 55' 35.5872'' N",
"Longitude":"77° 36' 4.16916'' E","Latency(max)":"18",
"Latency(min)":"17"
},
"TestStaus":"Passed",
"Test":"Game Test"};
Recursive function
function drawJSON(obj){
for(var key in obj){
if(typeof obj[key] === 'object'){
drawJSON(obj[key]);
continue;
}
document.write("<div><span>" + key + "</span><span>" + obj[key] + "</span> </div>");
}
}
drawJSON(vk);
DEMO
The Results is object so it is showing as [object object]. You can do this by:
function printEach(jsonObject) {
$.each(jsonObject, function(k, v)
{
if(typeof v === 'object') {
printEach(v);
} else {
console.log("<tr><td>" + k + "</td><td>" + v + "</td></tr>");
}
});
}
var vk = {"Results":{"Number of Tests":"2","Latency(avg)":"17","Failure":"0%","Latitude":"12° 55' 35.5872'' N","Longitude":"77° 36' 4.16916'' E","Latency(max)":"18","Latency(min)":"17"},"TestStaus":"Passed","Test":"Game Test"};
var jsonObject = JSON.stringify(vk);
printEach($.parseJSON(jsonObject));
You can see the fiddle http://jsfiddle.net/58grs/1/

Looping in my array of errors (key/value) in javascript

I have a variable with some data (key/value):
var errors = JSON.parse(xhr.responseText);
Here is the content:
{
"vehicle.Model":"Le champ Model est requis.",
"vehicle.Brand":"Le champ Brand est requis.",
"vehicle.Registration":"Le champ Registration est requis."
}
I would like to loop into it and display the key and the value for each.
How to proceed?
Thanks.
var key;
for (key in errors) {
if (errors.hasOwnProperty(key)) {
console.log(key + " : " + errors[key]);
}
}
for (var key in errors) {
var code = key;
var label = errors[key];
...
}
Note that there is no guarantee regarding the iteration order in today's ECMAScript.
I'd recommend you to read MDN's Working with objects.
for( key in errors ){console.log(key + " = " + errors.key);}

Returning result from jQuery's .each()

My "stack" is totally overflow, so I need a help :)
I trying to get all values from FORM and save them to associative array. Here is a code:
var dat = [];
$('form[name=' + form.name + '] input[name], form[name=' + form.name + '] select[name], form[name=' + form.name + '] textarea[name]').each(function(i,el) {
dat[$(this).attr('name')] = $(this).val();
});
I am waiting for all values become in dat after this piece of code, but it looks like dat is internal variable of .each() lambda function, so it is unavailable after .each() is completed.
How to return resulting dat[] from the cycle ?
Try this
function() getFormData(){
var dat = {};
$('form[name=' + form.name + ']').find('input[name], select[name], textarea[name]').each(function(i,el) {
dat[$(this).attr('name')] = $(this).val();
});
return dat;
}
This function will return a json object containing all the form elements name/value pair specified in the selector.

ReferenceError from a JavaScript function that is defined correctly?

I've declared myself a JavaScript object called "breakdown".
I've then borrowed a function which I found on the jQuery extend() documentation, which works perfectly well on a different page, but identical setup - rewards object instead of breakdown.
breakdown = {};
breakdown.printObj = function(obj) {
var arr = [];
$.each(obj, function(key, val) {
var next = key + ": ";
next += $.isPlainObject(val) ? printObj(val) : val;
arr.push( next );
});
return "{ " + arr.join(", ") + " }";
}
I'm then trying to use it as I have on the other page to see what is in my "categories" array:
breakdown.getPointsBreakdown = function(categories, transactions) {
alert( breakdown.printObj(categories) );
If I "typeof" that alert instead, it displays "object". If I alert "categories[1].Title", it displays "Good Behaviour", so the array is being passed to the categories variable in this function correctly.
However, when I use "breakdown.printObj", I get the following error in FireBug:
ReferenceError { message="printObj is not defined", fileName="https://frog.ashingtonh...7fa8452ccb3e94ba89e487a", more...}
I don't understand how!
Change
next += $.isPlainObject(val) ? printObj(val) : val;
to:
next += $.isPlainObject(val) ? breakdown.printObj(val) : val;
You should probably have breakdown.printObj(val) rather than just printObj(val) in line 6.
Change
breakdown.printObj = function(obj) {
// snip...
};
to
breakdown.printObj = function printObj(obj) {
// snip...
};
so that you can call it recursively.
This is called named function expression.

Categories