Each for object? [duplicate] - javascript

This question already has answers here:
Iterate over Object Literal Values
(8 answers)
Closed 3 years ago.
I have object in JavaScript:
var object = someobject;
Object { aaa=true, bbb=true, ccc=true }
How can I use each for this?
object.each(function(index, value)) {
console.log(value);
}
Not working.

A javascript Object does not have a standard .each function. jQuery provides a function. See http://api.jquery.com/jQuery.each/ The below should work
$.each(object, function(index, value) {
console.log(value);
});
Another option would be to use vanilla Javascript using the Object.keys() and the Array .map() functions like this
Object.keys(object).map(function(objectKey, index) {
var value = object[objectKey];
console.log(value);
});
See https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Object/keys and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
These are usually better than using a vanilla Javascript for-loop, unless you really understand the implications of using a normal for-loop and see use for it's specific characteristics like looping over the property chain.
But usually, a for-loop doesn't work better than jQuery or Object.keys().map(). I'll go into two potential issues with using a plain for-loop below.
Right, so also pointed out in other answers, a plain Javascript alternative would be
for(var index in object) {
var attr = object[index];
}
There are two potential issues with this:
1 . You want to check whether the attribute that you are finding is from the object itself and not from up the prototype chain. This can be checked with the hasOwnProperty function like so
for(var index in object) {
if (object.hasOwnProperty(index)) {
var attr = object[index];
}
}
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty for more information.
The jQuery.each and Object.keys functions take care of this automatically.
2 . Another potential issue with a plain for-loop is that of scope and non-closures. This is a bit complicated, but take for example the following code. We have a bunch of buttons with ids button0, button1, button2 etc, and we want to set an onclick on them and do a console.log like this:
<button id='button0'>click</button>
<button id='button1'>click</button>
<button id='button2'>click</button>
var messagesByButtonId = {"button0" : "clicked first!", "button1" : "clicked middle!", "button2" : "clicked last!"];
for(var buttonId in messagesByButtonId ) {
if (messagesByButtonId.hasOwnProperty(buttonId)) {
$('#'+buttonId).click(function() {
var message = messagesByButtonId[buttonId];
console.log(message);
});
}
}
If, after some time, we click any of the buttons we will always get "clicked last!" in the console, and never "clicked first!" or "clicked middle!". Why? Because at the time that the onclick function is executed, it will display messagesByButtonId[buttonId] using the buttonId variable at that moment. And since the loop has finished at that moment, the buttonId variable will still be "button2" (the value it had during the last loop iteration), and so messagesByButtonId[buttonId] will be messagesByButtonId["button2"], i.e. "clicked last!".
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures for more information on closures. Especially the last part of that page that covers our example.
Again, jQuery.each and Object.keys().map() solve this problem automatically for us, because it provides us with a function(index, value) (that has closure) so we are safe to use both index and value and rest assured that they have the value that we expect.

for(var key in object) {
console.log(object[key]);
}

var object = { "a": 1, "b": 2};
$.each(object, function(key, value){
console.log(key + ": " + object[key]);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
//output
a: 1
b: 2

Related

Why is for loop setting all jquery onclick to last iteration [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
I have the following code that works
var btns = $('.gotobtn');
$('#'+btns.get(0).id).click(function() {
document.querySelector('#navigator').pushPage('directions.html', myInfo[0]); });
$('#'+btns.get(1).id).click(function() {
document.querySelector('#navigator').pushPage('directions.html', myInfo[1]); });
$('#'+btns.get(2).id).click(function() {
document.querySelector('#navigator').pushPage('directions.html', myInfo[2]); });
// this works. I click on button 0 and get myInfo[0],
// on 1 and get myInfo[1], on 2 and get myInfo[2]
But replacing it with a loop does not work correctly. Instead, I always get the last element: myIfno[2] for any button I press.
var btns = $('.gotobtn');
var i = 0;
for (i = 0; i<3; i++){
var btnid = "#" + btns.get(i).id;
$(btnid).click(function() {
document.querySelector('#navigator').pushPage('directions.html', myInfo[i]); });
}
// this does set the buttons on-click but when I click on them,
// all get the latest iteration, in this example myInfo[2]
Why is this? And how do I fix that, without defining each button manually?
I want to see how to do it in jquery.
Because: JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script
Replace:
$(btnid).click(function() {
document.querySelector('#navigator').pushPage('directions.html', myInfo[i]);
});
With:
$(btnid).click(customFunction(i));
And declare this function outside the loop:
function customFunction(i) {
document.querySelector('#navigator').pushPage('directions.html', myInfo[i]);
}
your
$(btnid).click(function() {
document.querySelector('#navigator').pushPage('directions.html', myInfo[i]); });
Must be outside your for loop.
#ibrahim mahrir is correct. It's caused by the same phenomena as described in 46039325 although this question is specific for JQuery binding and will probably be useful to some. (I saw the unanswered question in several places on the web)
It happens because I'm binding to i, and in the meantime i has changed to the last iteration (which is 2 in this example). I need to bind to the value of i while iterating.
This will happen (due to quirks in javascript) if I define the binding to a parameter of a function. The parameter is "dynamically created" each time and the value of that param (during that iteration) will be bound.
So when I finally do click on the second button (id:1, the first is id:0), it will invoke the method with the value of 1, correctly.
Here's an example of how the fix looks in jQuery:
$(function(){ // document ready
function btnaction(i){
var btns = $('.gotobtn');
$('#'+btns.get(i).id).click(function() {
document.querySelector('#navigator').pushPage('directions.html', gotoInfo[i]);
});
}
and I call it in the loop
for (i = 0; i<6; i++)
btnaction(i);
Alls well that ends well...

Run the same function on each item in a list / array

Goal
I have a working function (JSFiddle). On numerous occasions throughout a script the function runs sequentially. In these instances, there is a lot of repetitious code that I would like to consolidate.
Ideally changing code like this:
functionName("First_item") +
functionName("Second_item") +
functionName("Third_item") +
To something like this:
functionName("First_item", "Second_item", "Third_item");
The function will run for each item in the list so the result is the same but the code more elegant and maintainable.
Notes:
I’m not looking to use any libraries (e.g. jQuery) to accomplish the goal.
Solution
Amit Joki’s answer kindly noted I could use arguments. When I implemented the code, the modified function (JSFiddle) only returned the output string for the first argument / item.
Vanice’s answer pointed out the eventual solution.
Make one string from the output of all arguments / items by concatenating (joining) the output strings within the for loop (with the use of +=).
Return the concatenated output by placing the return outside of the for loop.
Example
Working solution (JSFiddle).
Thanks
Thank you very much to everyone for their time and help on this. I really appreciate it!
Leveraging Javascript's Prototype OOP: You can add an each function to Array's themselves, so every array in your code that will automatically have an inhereted each function.
Array.prototype.each = function(callback){
for (var i = 0; i < this.length; i++){
callback(this[i]);
}
}
Usage:
myArray.each(myCoolFunction)
['something','somethingelse',somethingother'].each(myCoolFunction)
myArray.each( function (item) {
// if your item has a method
item.Something();
// if you'd like to call a function on the item:
doSomething(item);
});
caveats:
Because javascript is an asynchronous language that is interpreted differently across various browsers and inherently handles primitive objects and complex objects differently, it is highly recommended usage of underscore or lodash. You can also make your own, but you'll need ensure the objects passed through will be handled by the functions properly. This may include workarounds or creating special callback functions for different object types that are passed through your each function.
For more information: Is JavaScript a pass-by-reference or pass-by-value language?
Libraries you should seriously consider:
lodash:
https://lodash.com/docs#forEach
_([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
// → logs each number and returns '1,2,3'
_.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
// → logs each number and returns the object (property order is not guaranteed across environments)
underscore:
http://underscorejs.org/#each
_.each([1, 2, 3], alert);
=> alerts each number in turn...
You don't need an array. Just use arguments
function functionName(){
for(var i = 0; i < arguments.length; i++){
// do something with arguments[i];
}
}
and then you can do
functionName("shot_type","shot_height","shot_angle","framed","scene_depth");
P.S #codebox's solution works if supporting legacy IE version isn't a problem. Don't know why he deleted it...so putting it here so it helps. His answer using forEach
["shot_type","shot_height","shot_angle","framed","scene_depth"].forEach(FunctionName);
EDIT: Looking at your Fiddle, you have a return inside the for loop - therefore the function will return after the first iteration. Put the return after the for and concatenate the output to one string.
var output = "";
for(...){
output += description_of_object + ": " + randomly_selected_item_from_object + ".\n";
}
// return it
return output;
With Javascript only:
var actions = ["shot_type","shot_height","shot_angle","framed","scene_depth"];
for (var i = 0; i < actions.length; i++){
FunctionName(actions[i]);
}
With JQuery:
$.each(["shot_type","shot_height","shot_angle","framed","scene_depth"], function(index,value){
FunctionName(value);
});
I haven't tested it but it should work.
To avoide redundancy in code use an array with the values, that you want to pass through the function and call the function in an loop.
var vals=["shot_type","shot_height","shot_angle","framed","scene_depth"];
for(var i=0; i<vals.length; i++)
{
FunctionName(vals[i]);
}
If you want to expand the function (adding another parameter) you can just expand the for-loop and the array structure.
Alternatively you could fill an object with the values and handle this logic in an object. But this would just do a difference on calling the function.

Benefits of .each() function in jQuery over traditional "for" loops

One of my colleague suggested me to use jQuery .each() function over javascript for loop to traverse through DOM elements on my page, I am not a newbie in jQuery, but never understood the real reason behind why developers tend to use .each() over for loop of javascript. Can anyone explain it to me?
If you want to iterate using a for loop, you have to increment the index:
for(var i=0; i<arr.length; ++i) {
and then you have to get the actual value using the index:
var value = arr[i];
.each does both of these for you and passes the values into a function:
$(...).each(function(i, value) {
// actual interesting part of the loop...
});
It simply saves you the boilerplate code of incrementing the index and getting the value at that index.
The variables defined in an .each function are also closed over (i.e., within a closure), so the equivalent code (considering looping and variable closure, plus setting this, as well as breaking on a false return value) might be something like:
(function() {
for(var i=0; i<arr.length; ++i) {
var ret = (function(index, value) {
// actual interesting part of the loop...
}).call(arr[i], i, arr[i]);
if(ret === false) break;
}
})();
which is quite a bit more to type.
In terms of execution performance, .each is (unsurprisingly) slower than a raw for loop, because it does much more than a raw for loop.
Its very easy to use
But it is slow as shown in this test result.
http://jsperf.com/jquery-each-vs-for-loop/214
Because it is easier & cleaner to do
$jqExpr.each(function(i, el){
/* YOUR CODE */
});
than
for(var i=0; i < $jqQExpr.length; i++){
el = $jqExp[i];
/* YOUR CODE */
}
It's slower, but more expressive (shorter) and it also sets up closures. Also, on jQuery collections it integrates well into chaining; while for plain arrays I would suggest using the native .forEach method.
There is also, for me, an important benefit closure side effect if you use each instead of for.
Consider the code below (I'm using coffeescript as I'm found of..) which alerts on all links with its href value:
$("a").each (i, e)->
href = $(e).attr('href')
$(e).on "click" alert(href)
If you "translate" it into a simple for loop like :
for e in $("a")
href = $(e).attr('href')
$(e).on "click" alert(href)
This code won't work as the href variable is not enclosed in a closure...

Setting onclick to use current value of variable in loop [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
The title may not make sense and nor would a description alone, so here's a sample of the code:
for(var a=0;a<10;a++) {
var b=document.createElement('b')
b.onclick=function() {
alert(a)
}
b.innerHTML=a
document.body.appendChild(b)
}
What's odd is that when I set innerHTML to a, it uses the current value of a. So, this code creates ten <b> elements with the values 0, 1, 2, 3, 4, 5, 6, 7, 8, & 9. This works perfectly. What doesn't, however, is the onclick function. It returns the final value of a (10) when any of these elements is clicked. I've tried setting another variable to a and then using that other variable in the onclick event, but still it returns the final value of a. How would I make it use the value of a when onclick is set?
Try the following
b.onclick= function(arg) {
return function() {
alert(arg);
}
}(a);
The problem you were hitting is that javascript doesn't use block scope. Instead all variables have a lifetime of their containing function. So there was only ever one a captured in all of the onclick functions and they all see it's final value.
The solution works around this by creating a new function and value per scope and then immediately setting that value to the current value of a.
Here's a version that's a bit expanded and perhaps a bit more readable
var createClickHandler = function(arg) {
return function() { alert(arg); };
}
for(var a=0;a<10;a++) {
var b=document.createElement('b')
b.onclick = createClickHandler(a);
b.innerHTML=a
document.body.appendChild(b)
}
One dirty workaround which I found out is to use .value property of the created object to store the argument which we want to pass to .onclick function. So I can do something like
for(var a=0;a<10;a++) {
var b=document.createElement('b')
b.value=a
b.onclick=function() {
myfunc(this.value)
}
b.innerHTML=a
document.body.appendChild(b)
}
function myfunc(n){
console.log(n)
}
I am not sure if .value works with every element, but it does for DIV for example.
EDIT: To pass more values then one you can concatenate them using some character like _ and then split it
b.value=x+"_"+y+"_"+z
function myfunc(str){
t=str.split("_")
x=t[0]
y=t[1]
z=t[2]
console.log(x+","+y+","+z)
}

JavaScript JSON compare

There are two JSON var (JSON.parse'd already)
var acc http://pastebin.com/7DyfFzTx
var sit http://pastebin.com/vnZiVaDx
My objective is to loop each var to compare acc.items.site_name with sit.items.main_site.name to see if they are equal. If they are equal, then I need to store sit.items.main_site.site_url in a variable. I am using this code:
for(i=0; i < acc.items.length;i++)
{
aname = acc.items[i].site_name;
for(i=0; i < sit.items.length;i++)
{
sname = sit.items[i].main_site.name;
if (aname == sname)
alert("same "+aname);
}
}
But the alert only logs "same Physics" . "Physics" is the first object in acc.items. This means that the loop is only comparing first acc.items but how do I make it compare the second and further on objects (e.g. "TeX - LaTeX")
Well, you can start by using a different control variable for the outer and inner loops. :)
Also, notice how you are running through every element of the second object each time you consider an element in the first object- that doesn't seem like what you want to do.
Try reviewing this posting for a more modular method:
http://jsperf.com/recursive-vs-json-object-comparison
You're using the same variable for the inner and outer loop.
Give the second for loop a different variable name than i

Categories

Resources