Looping inside an object - javascript

I had encountered something unclear to me in the for loop:
var info = {
"full_name" : "John Doe",
"title" : "FrontEnd",
"links" : {
"blog" : "JohnDoe.com",
"facebook" : "http://facebook.com/JohnDoe",
"youtube" : "http://www.youtube.com/JohnDoe",
"twitter" : "http://twitter.com/JohnDoe"
}
};
I'm looping through this object with this loop:
var output = "";
for (var key in info.links ) {
if (info.links.hasOwnProperty(key)) {
output += '<li>' +
'<a href = "' + info.links[key] +
'">' + key + '</a>' +
'</li>';
console.log(key);
}
}
var update = document.getElementById('links');
update.innerHTML = output;
And my question is, what is var key in this loop and why it works when there is no var key in scope of this loop? In this case var key becomes blog, facebook etc. inside info.links object. But why?

Go thorough this https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
for (variable in object) { ...
}
Variable "A different property name is assigned to variable on each iteration."
In your case "facebook", "twitter" , etc are your property names in object

var means that you're declaring a new variable, key is the name you give this variable.
Since you're looping through everthing in info.links you want to be able to call each var individually

Look this for a sample solution. The key represents the keys of the info.links object say blog, facebook etc, thus info.links[key] represents the value i.e,
info.links["blog"] = "JohnDoe.com",
info.links["facebook"] = "http://facebook.com/JohnDoe"
Hope this is clear.
Look this link for loop description

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.

How do I extract JSON string using a JavaScript variable?

I am currently trying to retrieve the corresponding dial_code by using the name which I am obtaining as a variable.
The application uses a map of the world. When the user hovers over a particular country, that country is obtained using 'getRegionName'. This is then used to alter the variable name. How can I use the variable name to retrieve the dial_code that it relates to?
JSON
var dialCodes = [
{"name":"China","dial_code":"+86","code":"CN"},
{"name":"Afghanistan","dial_code":"+93","code":"AF"}
];
The following code runs on mouse hover of a country
var countryName = map.getRegionName(code);
label.html(name + ' (' + code.toString() + ')<br>' + dialCodes[0][countryName].dial_code);
This code doesn't work correctly. The dialCodes[0][countryName].dial_code is the part that is causing the error, but I'm not sure how to correctly refer to the corresponding key/value pair
If you have to support old browsers:
Loop over the entries in the array and compare to the given name:
var dialCode;
for(var i = 0; i < dialCodes.length; i++) {
if(dialCodes[i].name === countryName) {
dialCode = dialCodes[i].dial_code;
break;
}
}
label.html(countryName + ' (' + dialCode + ')');
If you browser support Array.prototype.filter:
dialCodes.filter(function(e) { return e.name === 'China' })[0].dial_code
If you have control over it, I recommend making your object more like a dictionary, for example if you are always looking up by the code (CN or AF) you could avoid looping if you did this:
var dialCodes = {
CN: { "name":"China","dial_code":"+86","code":"CN" },
AF: {"name":"Afghanistan","dial_code":"+93","code":"AF"}
};
var code = dialCodes.CN.dial_code;
Or
var myCode = 'CN'; // for example
var code = dialCodes[myCode].dial_code;
Since it's an array you can use filter to extract the data you need.
function getData(type, val) {
return dialCodes.filter(function (el) {
return el[type] === val;
})[0];
}
getData('code', 'CN').dial_code; // +86

Referencing a JavaScript variable

I have 2 variables in a script tag that get populated with string values.
I also have an .each loop that goes through each select box I have on the page. I want a way to append the index value of the loop to the name of the variable, and retrieve its value.
var secQuestion1 = "bla"
var secQuestion2 = "bla"
selectbox.each(function( index ) {
var question = ['secQuestion' + (index+1) ];
console.log("question = ", ['secQuestion' + (index+1)] )
});
I thought I might be able to use bracket notation to retrieve the value.
Any ideas on how to do this, so on each index in the loop I would get my questions values?
I'd use an array here, for example:
var arrQuestions = ["question A", "question B", "questionC"];
selectbox.each(function( index ) {
var question = arrQuestions[index];
}
it depends in what context your code is running, if your variables are global then you can do
window['secQuestion' + (index+1)]
you can also put them in an object and access them that way
var obj = {
secQuestion1: "bla",
secQuestion2: "bla"
};
selectbox.each(function( index ) {
console.log("question = ", obj['secQuestion' + (index+1)] )
});

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.

Is there an easy way to create dynamic variables with Javascript?

I've built a data-driven google map with different icons that get assigned to the map depending on the type of item located. So if I have 5 types of landmark, and each gets a different icon (store, library, hospital, etc.)-- what I'd like to do is generate the google icon objects dynamically. I was thinking something like this:
types = array('hospital','church','library','store',etc);
var i=0;
while (i<=types.length) {
var landmark + i = new google.maps.Icon();
landmark.image = "icon" + i + ".png";
i++;
}
however, as you've probably guessed, this doesn't work. I also tried using eval, like this:
while (i<=types.length) {
doIcon(i);
i++;
}
function doIcon(i){
eval("var landmark" + i + " = new.google.maps.Icon();");
return eval("landmark" + i);
}
but it didn't work either-- I'd appreciate any pointers on generating javascript variables dynamically. It's got to be pure js, I could do it in PHP but that's not an option here.
Thanks!
It's really easy to do: object["variablename"] = whatever;
So for example you could have an object: var Landmarks = {} and you could add to it like so: Landmarks["landmark" + i] = new google.maps.Icon(); and pass it that way.
If you need these variables to be global (why would you?) you can access the global object directly using window.
If you're going to do it using a declared object such as Landmark["landmark" + i], you really may as well use an index array rather than an associative, it's much easier for iteration. Objects aren't really used with indexed properties because Arrays do a much better job of it:
var myObj = // object version
{
"item0": "blah",
"item1": "blah"
// etc
}
var myArr = // array version
[
"blah",
"blah"
// etc
]
Much more sensible to use the array:
landmarks = []; // new array
types = array('hospital','church','library','store',etc);
var i=0;
while (i<=types.length) {
landmarks.push(new google.maps.Icon());
landmarks[i].image = "icon" + i + ".png";
i++;
}
It makes more sense to do it that way and for...in loops on objects can get a bit messy with prototyped properties being enumerable, etc.
If you're trying to make a variable global, add it to the window object:
var myCustomVar = "landmark" + i;
window[myCustomVar] = new google.maps.Icon();
alert(landmark0);
But this would be polluting the global namespace with many unnecessary variables. So you'd still be better with an array:
window.landmarks = [];
landmarks.push(new google.maps.Icon());
// etc...
Just to answer your question directly (although please note that this is not the solution you want. Check out the other answers. This is just for documentation!), here's a copy-paste from a JavaScript console:
> window["myNamedVar"] = "Hello, World!";
> console.log(myNamedVar);
"Hello, World!"
You'd be better off creating a javascript object which you can use somewhat like an associative array is used in PHP:
var types = ['hospital','church','library','store'];
var landmarks= {};
for (var i in types) {
landmarks[types[i]]= new google.maps.Icon();
landmarks[types[i]].image = "icon" + i + ".png";
}
alert(landmarks['hospital'].image); // displays "icon0.png"
Do you really need those variables? Can't you do with this:
var types = ['hospital','church','library','store'];
for(var i =0; i < types.length; i += 1) (new google.maps.Icon()).image = "icon" + i + ".png";
Modifications done based on comment:
icon name pattern changed from icon + index + .png to icon + type + .png
and saving the results of the loop.
types = ['hospital','church','library','store'];
var landmarks = {};
// images names are of the form icon + type + .png
function createIcon(type)
{
var icon = new google.maps.Icon();
icon.image = "icon" + type + ".png";
return icon;
}
// mapping of landamarks by type and icon
for (var i = 0, len = types.length; i < len; i++)
{
landmarks[types[i]] = createIcon(types[i]);
}
the result is :
{
hospital : icon,
church : icon,
...
}
where icon is a google map icon that has an image attribute that is a string of the form "icon{type}.png" , e.g, iconhostpital.png, iconchurch.png.
To use the icons write landmarks.type where type is one the names in the array of types, e.g. landmarks.hospital.
if the image names are of the form icon + number + .png, and the number for each type is equivalent to its index in the array replace the call createIcon(type[i]) for createIcon(i).

Categories