JS Tip of the Day: Object.keys vs Array.keys

Object.keys vs Array.keys
Version: ES2015
Level: Intermediate

Object has a static method, Object.keys(), which will return all of the enumerable, non-inherited keys of any object as an array. This can be used to easily iterate over all of the property names of an object.

let petTypeByName = {
    Tweety: 'bird',
    Sylvester: 'cat',
    Hector: 'dog'
};

let names = Object.keys(petTypeByName);
console.log(names); // [ 'Tweety', 'Sylvester', 'Hector' ]

for (let name of names) {
    console.log(name);
}
/* logs:
Tweety
Sylvester
Hector
*/

Related methods include Object.values() and Object.entries() for getting an object’s values or key-value pairs respectively.

for (let type of Object.values(petTypeByName)) {
    console.log(type);
}
/* logs:
bird
cat
dog
*/

for (let pair of Object.entries(petTypeByName)) {
    console.log(pair);
}
/* logs:
[ 'Tweety', 'bird' ]
[ 'Sylvester', 'cat' ]
[ 'Hector', 'dog' ]
*/

Arrays (as well as Maps and Sets) also have their own keys() method which is accessible from their instances. This can similarly be used to iterate through the keys of an array.

let names = ['Tweety', 'Sylvester', 'Hector'];
for (let index of names.keys()) {
    console.log(index);
}
/* logs:
0
1
2
*/

However, there is an important difference between Object.keys() and an array’s keys() method. While Object.keys() returns an array, keys() for array instances returns an iterator.

let names = ['Tweety', 'Sylvester', 'Hector'];
console.log(Object.keys(names)); // [ '0', '1', '2' ]
console.log(names.keys()); // Array Iterator {}

The same applies to the values() and entries() methods. The instance method versions of these functions (for arrays as well as Map and Set instances) return iterators instead of arrays.

let names = ['Tweety', 'Sylvester', 'Hector'];
console.log(names.keys()); // Array Iterator {}
console.log(names.values()); // Array Iterator {}
console.log(names.entries()); // Array Iterator {}

In the case of arrays, these instance methods also account for all possible values from 0 up to the array length, which for sparse arrays, can account for array elements that don’t actually exist. The static methods on Object will only identify existing values.

let names = [];
names[2] = 'Hector';

for (let index of names.keys()) {
    console.log(index);
}
/* logs:
0
1
2
*/

for (let index of Object.keys(names)) {
    console.log(index);
}
/* logs:
2
*/

You may notice that if you iterate through an array directly using a for...of loop, you’ll see that it will also account for all possible values from 0 up to the length. In fact, the implementation of the iterator used in for…of loops for arrays is the same implementation as the array values() method.

let names = [];
names[2] = 'Hector';

for (let name of names) {
    console.log(name);
}
/* logs:
undefined
undefined
Hector
*/

console.log(names[Symbol.iterator] === names.values); // true

If you wanted an array of values matching those produced by the array keys() (or similar) method, you’d have to spread it into an array to get it since iterator objects are not themselves arrays or even array-like.

let names = [];
names[2] = 'Hector';

console.log([...names.keys()]); // [ undefined, undefined, 'Hector' ]

While there is no built-in way to get an iterator for object properties, there is a proposal (stage 1) for improved object iteration that would add the following static methods to Object, each providing an iterator rather than an array:

  • Object.iterateKeys()
  • Object.iterateValues()
  • Object.iterateEntries()

These could provide benefit from not having to create an intermediary array for iteration through object properties.

More info:


More tips: JavaScript Tips of the Day

1 Like