Javascript for...in Loops in Chrome Sample Extension - javascript

I was editing Chrome's sample oauth contacts extension
when I came across an interesting for-loop in line 7 of contacts.js:
for (var i = 0, contact; contact = contacts[i]; i++) {
variable i was never used in the body of the for loop, so it seemed like a typical "for...in" loop. I tried replacing the for-loop with
for (contact in contacts) {
but when I ran the extension, all my contacts came back undefined
Here is the full for-loop from the extension
for (var i = 0, contact; contact = contacts[i]; i++) {
var div = document.createElement('div');
var pName = document.createElement('p');
var ulEmails = document.createElement('ul');
pName.innerText = contact['name'];
div.appendChild(pName);
for (var j = 0, email; email = contact['emails'][j]; j++) {
var liEmail = document.createElement('li');
liEmail.innerText = email;
ulEmails.appendChild(liEmail);
}
div.appendChild(ulEmails);
output.appendChild(div);
}

What the code given does
What that does is evaluate what contacts[i] is and whether it is truthy or not, while at the same time caches the array element of the applicable index.
It's equivalent to the following code (note that in this example ++i has the same side effect as i++):
for (var i = 0; contacts[i]; ++i)
{ var contact = contacts[i];
// use contact
}
This could be interpreted as something like the following:
If !contacts[i] is false (i.e. it is truthy) continue the loop.
Otherwise, end the loop (it is falsy).
If the goal of that code was to iterate through all of an array, the problem would be that if you wanted to iterate through an element but it was falsy, it would end the entire loop instead of performing the (likely) intended effect. Take this example:
var foo = [1, 3, 5, 7, 9, 0, 2, 4, 6, 8];
// example for-loop given
for (var i = 0; foo[i]; ++i)
{ var bar = foo[i];
console.log('example: ' + bar);
}
// "normal" way of iterating through array
for (var i = 0, l = foo.length; i < l; ++i)
{ var bar = foo[i];
console.log('normal: ' + bar);
}
You'd find that the example only logs up to the number 9, while the "normal" way goes through the entire array. Of course though, if you could guarantee that all values within the array would be truthy (e.g. all array elements are objects), then this isn't that much of an issue.
What for-in does and why it doesn't work
You tried to replace that code with the following:
for (contact in contacts) { /*code here*/ }
However, this doesn't work for a number of reasons:
contact is a string of the property name, not the value of it. Take this example:
var foo =
{ bar1: 1
, bar2: 2
, bar3: 3
, bar4: 4
, bar5: 5 };
for (var i in foo) console.log(i);
What you get back is the property name (i.e. "bar1, bar2...") instead of the value. To do so for an object, you'd have to do something like the following:
for (var i in foo)
{ var bar = foo[i];
console.log(bar);
}
Now you should get back "1,2,3,4,5" on separate lines. If you got that, and some other things, you might be have defined items on Object.prototype - which is why it's generally a bad idea, unless it really makes the code cleaner, and there is a substantial purpose for doing so. To filter these out, add a hasOwnProperty() check:
for (var i in foo) if (foo.hasOwnProperty(i))
{ var bar = foo[i];
console.log(bar);
}
The upcoming version of ECMAScript (the "standard" version of JavaScript, minus the DOM) will have something called for-of loops, which will make it easier to do this sort of thing.
For-in loops generally aren't meant for arrays (it is possible, but it's just not a good idea usually). If you need to use for-in, you probably should be using an object instead - all arrays are objects, just that arrays have special internal length property and a few other things.
contact is an implied global, big no-no. In fact, implied globals are banned in strict mode. Use a variable declaration (inside or outside the for-in loop, doesn't matter) to solve this issue.
It's just learning about how JavaScript works and where to apply its various methods of doing things - some are more suitable than others in particular situations.

Here you are using an array,not an object.
Though using for..in outputs the same result as a normal for loop,this would be my answer.
MyRecommendation:
Use for..in for iterating over objects:
for..in iterates over properties of an object.
Note:the order of iteration is arbitary.
var Myobj = {
a: 1,
b: 2,
c: 3
};
for ( var prop in Myobj ) {
console.log(prop); // a...b...c
console.log(Myobj[prop]); // 1...2...3
}
but with this the problem is it will continue searching for enumerable properties up the prototype chain.So unless you dont use hasOwnProperty,it will iterate over local object and the prototype it is attached to.
//Improved version of above code:
for (var prop in Myobj) {
if ( Myobj.hasOwnProperty(prop) ) {
// prop is actually obj's property (not inherited)
console.log(prop); // a...b...c
console.log(Myobj[prop]); // 1...2...3
}
}
Use for loop for iteration over an array
for loop iterates over an array in sequential way.

Related

Is there any point in creating a custom iterator for an array in node.js?

I need to parse an 80GB+ CSV file, and thought this a good opportunity to understand iterators in JavaScript (and then probably use an existing library such as csv-iterator, fast-csv, etc).
Looking at an iterator example on MDN HERE, I see this code:
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
Which is pretty self-explanatory. I can create an iterator for an array:
var iteratorForMyArray = makeIterator([1,2,3,4,5,6,7,8,9, etc])
And then I can use my shiny new iterator to 'iteratively' pull out values of my array:
var val0 = iteratorForMyArray.next().value
var val1 = iteratorForMyArray.next().value
etc
I can see why this is useful when parsing a CSV file. I'm wondering if there is any point in creating such an iterator for a simple array?
I often find that while simplified examples are useful for understanding, it's sometimes hard to see when the example is only useful as an example vs is actually useful in programming.
Because JavaScript already provides 2 mechanisms for creating iterators over array structures:
1: Basic for loop
for (let i = 0, i < [1,2,3,4,etc]; i++) {
...
}
2: Array.prototype.forEach
[1,2,3,4,etc].forEach(function(val, i, arr) {
...
})
(which I have just seen is slow on this site)
Questions:
Does my custom iterator offer anything that these iterators don't?
Do both these 'in-house' iterators create 'iterators' (i.e. what I understand as sequential pointers to values in data structures)? Or am I off by miles...
ForEach guarantees order, as well as it'll skip invalid indexes etc. Your iterator doesn't provide anything extra when compared to a standard for loop, it'll just be slower, as you're calling a function to get the next item, where a basic for loop doesn't have this overhead. Use a for loop and cache the length to start for better performance.
One good feature of iterators is that each call gets the next element, whereas forEach it's all or nothing (you can't exit early), and with both a for loop and forEach all the logic must be inside the loop.
Consider an array iterator like:
function arrayIterator(array) {
// Get array indexes as an array
var indexes = Object.keys(array)
.filter(
// Test for valid Array index property
key => key == +key && key >= 0 && key.length == String(+key).length)
// Sort as numbers
.sort(function(a, b){return a-b});
// Parameters held in closure
var current = 0;
var count = 0;
var maxCount = indexes.length;
// Return iterator function
return function() {
return ++count > maxCount? 'done' : array[indexes[current++]];
};
}
var arr = [0,1,,,4,5,6];
// Add non-index numeric property
arr['01'] = '01';
// Add indexable numeric property
arr['10'] = '10';
// Add some other property
arr.foo = 'foo';
var x = arrayIterator(arr);
console.log(x()); // 0
console.log(x()); // 1
console.log(x()); // 4
console.log(x()); // 5
console.log(x()); // 6
console.log(x()); // 10
console.log(x()); // 'done'
console.log(x()); // 'done'
I'm sure there's more to be done in regard to checking valid indexes and also testing that an index still exists when the iterator is called, but is shows the concept.
There also needs to be some documentation about what happens when the array is modified between calls.

Can't count number of instances of specific property in JSON

My JSON is being returned by an ajax request and is stored in the data variable.
The JSON looks like this (the top line is a postcode / zip code and is different for every request):
{
"ML1 4EQ":{
"ExchangeCode":"WSMOT",
"ExchangeName":"MOTHERWELL",
"Options":{
"10":{
"Preference":"3rd Party Tail (TTB)",
"Supplier 1":9591,
"Supplier 2":3581,
"Wholesale":5200,
"RRP":6500
},
and so on for other 9 more Options.
}
}
}
I am trying to count the number of Options being returned but everything I've tried from reading other questions doesn't seem to work from returning undefined, to it only returning 3 instead of 10 (think I was counting the wrong level).
These include
var key, results = 0;
for (var k in data) { // only simple cross browser way to get the first property
var obj = data[k];
for (key in obj) {
results++;
count = results;
}
return; // no need to go further, we have counted the options in the postcode object
}
This will count the total number of options inside the first object.
var count = 0;
for(var key in data){
for(var i in data[key].Options){
count++;
}
break;
}
Fiddle
Counting the number of properties in an Object isn't wholly straightforward. There isn't a property that will directly tell you, and if you for...in over them to count them you also get inherited properties, so if anyone's defined anything on Object.prototype you'll get the wrong answer.
In ECMAScript Fifth Edition you get getOwnPropertyNames which returns an array of non-inherited property names:
var options= {'1': 'a', '2': 'b'};
Object.getOwnPropertyNames(options).length; // 2
For browsers that don't support Fifth Edition yet (primarily IE<=8) you can shim it:
if (!('getOwnPropertyNames' in Object)) {
Object.getOwnPropertyNames= function(o) {
var names= [];
for (var k in o)
if (Object.hasOwnProperty(k))
names.push(k);
return names;
};
}
However, if you have control over the format of the JSON output, I would strongly suggest turning your Options Object into a plain Array, which would seems to model your data much better. With an Array you can simply use Options.length.

JavaScript: Get first and only property name of object

If I want to enumerate the properties of an object and want to ignore prototypes, I would use:
var instance = { ... };
for (var prop in instance) {
if (instance.hasOwnProperty(prop)) {
...
}
}
What if instance only has one property, and I want to get that property name? Is there an easier way than doing this:
var instance = { id: "foobar" };
var singleMember = (function() {
for (var prop in instance) {
if (instance.hasOwnProperty(prop)) {
return prop;
}
}
})();
Maybe Object.keys can work for you. If its length returns 1, you can use yourObject[Object.keys[0]] to get the only property of the object. The MDN-link also shows a custom function for use in environments without the keys method1. Code like this:
var obj = {foo:'bar'},
kyz = Object.keys(obj);
if (kyz.length === 1){
alert(obj[kyz[0]]); //=> 'bar'
} else {
/* loop through obj */
}
1 Some older browsers don't support Object.keys. The MDN link supplies code to to make it work in these browsers too. See header Compatibility in the aforementioned MDN page
Shortest form:
instance[Object.keys(instance)[0]];
ES6+ function:
let first = v => v[Object.keys(v)[0]];
Use the function:
first({a:'first', b:'second'}) // return 'first'
var foo = {bar: 1};
console.log(Object.keys(foo).toString());
which will print the string
"bar"
Though my answer is downvoted, it's still worth to know that there is no such thing as order of keys in javascript object. Therefore, in theory, any code build on iterating values can be inconsistent. One approach could be creating an object and to define setter which actually provides counting, ordering and so on, and provide some methods to access this fields. This could be done in modern browsers.
So, to answer you question, in general you approach is still most closs-browser. You can iterate using lodash or any other modern framework wich will hide "hasOwnProperty" complexity from you. As of August'15 Object.keys can be accepted as cross-browser and universal. After all IE8 happened years ago. Still there are some cases when you just don't wont store all set of keys in array. But I'd go with Object.keys - it's more flexible compared to iteration.
Unfortunately, there is no, "list properties" function built in, and there certainly isn't a "getFirstProperty" (especially since there is no guarantee that any property will consistently be "first").
I think you're better off writing a function like this one:
/**
* A means to get all of the keys of a JSON-style object.
* #param obj The object to iterate
* #param count maximum length of returned list (defaults to Infinity).
*/
function getProperties( obj, count )
{
if( isNaN( count ) ) count = Infinity
var keys = []
for( var it in obj )
{
if( keys.length > count ) break;
keys.push( it );
}
return keys;
}
Then, you could access the name though:
instance = {"foo":"bar"}
// String() on an array of < 2 length returns the first value as a string
// or "" if there are no values.
var prop = String(getProperties(instance, 1));
This is an old post, but I ended up writing the following helper function based on Object.keys().
It returns the key and value of the first property.
getFirstPropertyKeyAndValue(sourceObject) {
var result = null;
var ownProperties = Object.keys(sourceObject);
if (ownProperties.length > 0) {
if (ownProperties.length > 1) {
console.warn('Getting first property of an object containing more than 1 own property may result in unexpected results. Ordering is not ensured.', sourceObject);
}
var firstPropertyName = ownProperties[0];
result = {key: firstPropertyName, value: sourceObject[firstPropertyName]};
}
return result;
}
Answers in here all good, and with the caveat that the order may be unreliable (although in practice it seems the order the properties are set tends to stay that way), this quick and dirty method also works:
var obj = {foo: 1, bar: 2};
for(var key in obj) {
//you could use key here if you like
break;
}
//key now contains your first key
or a shorter version should also do it:
for(var key in obj) break;
//key now contains your first key

JavaScript object literal length === undefined?

I am working on this animation function but I have a problem. I can't seem to perform what should be an easy task, I can not get the length of an object. If you check out that jsFiddle you can see that I am running alert(properties.length); and it is returning undefined. Can anyone see why this might be?
This is supported in node.js and newer environments.
var obj = {a: "a", b: "b"};
Object.keys(obj).length // 2
JavaScript object simply do not have a length property, only Arrays do. If you want to know the number of properties that are defined on a object, you have to iterate over them and count them.
Also, your for in loop is prone to bugs due extension of Object.prototype since in will traverse the complete prototype chain and enumerate all the properties that are on the chain.
Example
// Poisoning Object.prototype
Object.prototype.bar = 1;
var foo = {moo: 2};
for(var i in foo) {
console.log(i); // logs both 'moo' AND 'bar'
}
You have to use the hasOwnProperty method on the object in order to filter out those unwanted properties.
// still the foo from above
for(var i in foo) {
if (foo.hasOwnProperty(i)) {
console.log(i); // only logs 'moo'
}
}
Many JavaScript frameworks out there extend the prototype, not using hasOwnProperty often leads to horrible bugs.
Update
Concerning the actual problem that your code is not animation both properties.
for(var p in properties) {
...
for(var i = 0; i <= frames; i++)
{
setTimeout((function(exti, element) {
return function() {
// p gets overriden by for outer for in loop
element.style[p] = original + (pixels * exti) + 'px';
}
// you need to pass in a copy of the value of p here
// just like you do with i and element
})(i, element), i * (1000 / 60), element);
}
....
}
If you are using Underscore.js, you can use _.size():
_.size({one : 1, two : 2, three : 3});
=> 3
Objects have no length, you'll need to use an array if you want that.
If you have to find the number of properties in an object there is only one way:
var length =0;
for(var i in obj) length++;
Here's #Junaid Qadir Shekhanzai's general function for "finding the length of an object" (which as we're told, should properly be called "counting the properties of an object"). It combines solutions from #Ivo Wetzel and #Martin Jespersen:
function countProperties(myObj){
var length = 0;
if(typeof myObj != 'object'){
return false;
}
for(var i in myObj) {
length++;
}
return length;
}

JavaScript: For...Each / With [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
JavaScript foreach Vs for
What's the difference between a for loop and for...in? I mean, if there is a difference, it can't be much.
And, I see within validation scripts sometimes the function written like:
function check() {
with(something) {
if(){
// do something
}
}
}
What's the point of the "with" condition?
The for each..in statement iterates a specified variable over all values of an object's properties. For each distinct property, a specified statement is executed. This was introduced by Mozilla in JavaScript 1.6 (see comment by #CMS below), and is not supported in all mainstream browsers.
For each example:
var sum = 0;
var obj = {prop1: 5, prop2: 13, prop3: 8};
for each (var item in obj) {
sum += item;
}
console.log(sum); // prints "26", which is 5 + 13 + 8
A similar statement is for..in, which iterates over the property names instead of property values. The same example written using for..in:
var sum = 0;
var obj = {prop1: 5, prop2: 13, prop3: 8};
for (var prop in obj) {
sum += obj[prop];
}
console.log(sum); // prints "26", which is 5 + 13 + 8
The for..in statement has been around since JavaScript 1.0, so you're safe to use it in all browsers.
These statements are different from the traditional for loop, since they are used to iterate over the properties of an object. A for loop can be used to iterate over the elements of an array, but it cannot be used to iterate over the properties of an object, unless you can use ECMAScript 5's Object.keys, which could be used to implement the above example as follows:
var sum = 0;
var obj = {prop1: 5, prop2: 13, prop3: 8};
var keys = Object.keys(obj)
for (var i = 0; i < keys.length; i++) {
sum += obj[keys[i]];
}
console.log(sum); // prints "26", which is 5 + 13 + 8
As for the with statement, note the following:
JavaScript looks up an unqualified name by searching a scope chain associated with the execution context of the script or function containing that unqualified name. The 'with' statement adds the given object to the head of this scope chain during the evaluation of its statement body. If an unqualified name used in the body matches a property in the scope chain, then the name is bound to the property and the object containing the property. Otherwise a 'ReferenceError' is thrown.
Therefore, consider the following:
var prop1 = 10;
var obj = {prop1: 5, prop2: 13, prop3: 8};
with (obj) {
console.log(prop1); // prints 5 instead of 10
}
Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.
for each (var i in obj) iterates over the values of an object while for(var i in obj) iterates over the properties. Use it for objects only!
You need a for...in... loop to iterate over properties of an object. A normal for loop would not help you here.
The with statement pushes the properties of the provided object at the beginning of the scope chain. In your example, if something has a property foo, then you can access this property inside the with body just with foo (instead of something.foo).
But note that all other variables, like local variables are now farther down the scope chain, which makes accessing them potentially slower.
Most books and experts recommend to not use with.

Categories

Resources