This post explains three approaches for extracting information from arrays and objects:
- for loops,
- array methods (courtesy of ECMAScript 5 [1]),
- listing property keys.
It concludes with best practices for applying these approaches.
for loops
All for loops can be used with the following statements.
for
Syntax:
for ([start]; [condition]; [final-expression])
statement
Rules:
- Traditional way of iterating over arrays.
- Can use var, but scope is always the complete surrounding function.
Example:
var arr = [ "a", "b", "c" ];
for(var i=0; i < arr.length; i++) {
console.log(arr[i]);
}
for...in
Syntax
for (variable in object)
statement
Rules:
- Iterate over property keys, including inherited ones.
- Don’t use for arrays. It iterates over both array indices and property keys. There will thus be problems as soon as someone adds a property to an array.
- Can use var, but scope is always the complete surrounding function.
- Properties can be deleted during iteration.
Pitfall: Iterates over both array indices and property keys.
> var arr = [ "a", "b", "c" ];
> arr.foo = true;
> for(var key in arr) { console.log(key); }
0
1
2
foo
Pitfall: Iterates over inherited properties.
function Person(name) {
this.name = name;
}
Person.prototype = {
describe: function() {
return "Name: "+this.name;
}
};
var person = new Person("Jane");
for(var key in person) {
console.log(key);
}
Output:
name
describe
Skip inherited properties: via
hasOwnProperty().
for(var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}
for each...in
Non-standard (Firefox only), iterates over the values of an object. Don’t use it.
Array methods for iteration
Iterate
Iterate over the elements in an array. The methods don’t have a result, but you can produce one in the callback as a side effect. They all have the following signature:
function(callback, [thisValue])
Parameters:
Iteration methods:
- Array.prototype.forEach() is similar to for...in, but only iterates over an object’s own properties.
- Array.prototype.every(): returns true if the callback returns true for every element.
- Array.prototype.some(): returns true if the callback returns true for at least one element.
Example:
var arr = [ "apple", "pear", "orange" ];
arr.forEach(function(elem) {
console.log(elem);
});
Pitfall: forEach() does not support
break. Use
every() instead:
function breakAtEmptyString(arr) {
arr.every(function(elem) {
if (elem.length === 0) {
return false; // break
}
console.log(elem);
return true; // don’t forget!
});
}
every() returns
false if a break happened and
true, otherwise. This allows you to react to the iteration finishing successfully (something that is slightly tricky with
for loops). Caveat: You need to return a “true” value to keep going. If you want to avoid that, you can use
some() and return
true to break:
function breakAtEmptyString(arr) {
arr.some(function(elem) {
if (elem.length === 0) {
return true; // break
}
console.log(elem);
// implicit: return undefined (interpreted as false)
});
}
Transform
Transformation methods take an input array and produce an output array, while the callback controls how the output is produced. The callback has the same signature as for iteration:
function([element], [index], [collection])
Methods:
- Array.prototype.map(callback, [thisValue]): Each output array element is the result of applying callback to an input element.
- Array.prototype.filter(callback, [thisValue]): The output array contains only those input elements for which callback returns true.
Reduce
For reducing, the callback has a different signature:
function(previousElement, currentElement, currentIndex, collection)
Methods:
- Array.prototype.reduce(callback, [initialValue]): Compute a value by applying callback to pairs (previousElement, currentElement) of array elements.
- Array.prototype.reduceRight(callback, [initialValue]): Same as reduce(), but from right to left.
Example:
// Sum of all array elements:
[17, 5, 4, 28].reduce(function(prev, cur) {
return prev + cur;
});
Listing property keys
- Object.keys(obj): Lists all enumerable own property keys of an object. Example:
> Object.keys({ first: "John", last: "Doe" })
[ 'first', 'last' ]
- Object.getOwnPropertyNames(): Lists all own property keys of an object, including non-enumerable ones.
> Object.getOwnPropertyNames(Number.prototype)
[ 'toExponential'
, 'toString'
, 'toLocaleString'
, 'toPrecision'
, 'valueOf'
, 'toJSON'
, 'constructor'
, 'toFixed'
]
> Object.keys(Number.prototype)
[]
Comment: The main reason that prototype methods are not enumerable is to hide them from iteration mechanisms that include inherited properties.
Best practices
Iterating over arrays
Options:
- Simple for loop.
- One of the iteration methods.
- Never use for...in or foreach...in.
Iterating over objects
Options:
Other tasks:
- Iterate over the property (key,value) pairs of an object: Iterate over the keys, use each key to retrieve the corresponding value. Other languages make this simpler, but not JavaScript.
Related reading
- What’s new in ECMAScript 5