How to Iterate Over JavaScript Object Entries


This post includes different ways for iterating over JavaScript Object entries and a performance comparison of those techniques.

Technique 1 : Object.entries

Object.entries() returns an iterable list of key, value pairs. This list counts only enumerable properties and doesn't include properties from prototype chain.

What are Enumerable Properties?
Properties created via simple assignment or via a property initializer

let obj = {
key1: "value1",
key2: "value2",
key3: "value3"
}
//convinient forEach
Object.entries(obj).forEach(entry => {
let key = entry[0];
let value = entry[1];
//use key and value here
});
//traditional for loop
let entries = Object.entries(obj);
for(let i = 0; i< entries.length;i++){
let key = entries[i][0];
let value = entries[i][1];
}

Technique 2 : Object.keys

Object.keys() returns an array of object keys. However, this function returns only enumerable properties.

Set of keys, returned from this method can be iterated in many different ways. Based on the performance comparison of array iteration techniques, while forEach being the most convenient method, traditional for loop outperforms every other technique.  Hence all object iteration techniques that requires array iteration will be compared with both forEach and traditional loop.

let obj = {
key1: "value1",
key2: "value2",
key3: "value3"
}
//convinient forEach
Object.keys(obj).forEach(key => {
let value = obj[key];
//use key and value here
});
//traditional for loop
let keys = Object.keys(obj);
for(let i = 0; i< keys.length;i++){
let value = obj[keys[i]];
}
view raw object_keys.js hosted with ❤ by GitHub

Technique 3 : Object.values

Object.values() returns an array of object property values. This function returns values of enumerable properties only.
let obj = {
key1: "value1",
key2: "value2",
key3: "value3"
}
//convinient forEach
Object.values(obj).forEach(value => {
//use value here
});
//traditional for loop
let values = Object.values(obj);
for(let i = 0; i<values.length;i++){
let value = values[i];
}

Technique 4 : for...in loop

for...in loop can be used to iterate over enumerable properties of JavaScript objects. This loop includes inherited properties from prototype chain.
let obj = {
key1: "value1",
key2: "value2",
key3: "value3"
}
for (const key in obj) {
let value = obj[key];
//optional check for properties from prototype chain
if (obj.hasOwnProperty(key)) {
//no a property from prototype chain
}else{
//property from protytpe chain
}
}
view raw for_in.js hosted with ❤ by GitHub

Technique 5 : Object.getOwnPropertyNames

Object.getOwnPropertyNames returns all the properties of an object including non enumerable properties. The result set will also include properties inherited from prototype chain.
let obj = {
key1: "value1",
key2: "value2",
key3: "value3"
}
// convinient forEach
Object.getOwnPropertyNames(obj).forEach(key => {
let value = obj[key];
//use key and value here
});
// traditional for loop
let props = Object.getOwnPropertyNames(obj);
for(let i = 0; i<props.length;i++){
let value = obj[props[i]];
}
view raw get_own_prop.js hosted with ❤ by GitHub

Performance Comparison

In order to compare the performance of each of above techniques, following script was executed for 1000 objects having 1 million properties in each.
const { PerformanceObserver, performance } = require('perf_hooks');
let objectSize = 1000000;
let iterations = 100;
console.log("Starting performance test with %d object size and %d iterations",
objectSize, iterations);
let values = {
ENTRIES: 0,
ENTRIES_FOR: 0,
KEYS: 0,
KEYS_FOR: 0,
VALUES: 0,
VALUES_FOR: 0,
FORIN: 0,
GETOWP: 0,
GETOWP_FOR: 0
}
const obs = new PerformanceObserver((items) => {
let entry = items.getEntries()[0];
console.log(entry.name, entry.duration);
values[entry.name] += entry.duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
function generateObject() {
let obj = {};
for (let i = 0; i < objectSize; i++) {
obj['key' + i] = 'val' + i;
}
return obj;
}
for (let i = 0; i < iterations; i++) {
let obj = generateObject();
//Object.entries
performance.mark('A');
Object.entries(obj).forEach(entry => {
let key = entry[0];
let value = entry[1];
});
performance.mark('B');
performance.measure('ENTRIES', 'A', 'B');
//Object.entries with for
performance.mark('A');
let entries = Object.entries(obj);
for (let i = 0; i < entries.length; i++) {
let key = entries[i][0];
let value = entries[i][1];
}
performance.mark('B');
performance.measure('ENTRIES_FOR', 'A', 'B');
//Object.Keys
performance.mark('A');
Object.keys(obj).forEach(key => {
let value = obj[key];
});
performance.mark('B');
performance.measure('KEYS', 'A', 'B');
//Object.Keys with for
performance.mark('A');
let keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
let value = obj[keys[i]];
}
performance.mark('B');
performance.measure('KEYS_FOR', 'A', 'B');
//Object.Values
performance.mark('A');
Object.values(obj).forEach(value => {
});
performance.mark('B');
performance.measure('VALUES', 'A', 'B');
//Object.Values with for
performance.mark('A');
let values = Object.values(obj);
for (let i = 0; i < values.length; i++) {
let value = values[i];
}
performance.mark('B');
performance.measure('VALUES_FOR', 'A', 'B');
//For In
performance.mark('A');
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
let value = obj[key];
}
}
performance.mark('B');
performance.measure('FORIN', 'A', 'B');
//Object.getOwnPropertyNames
performance.mark('A', i);
Object.getOwnPropertyNames(obj).forEach(key => {
let value = obj[key];
});
performance.mark('B');
performance.measure('GETOWP', 'A', 'B');
//Object.getOwnPropertyNames with for
performance.mark('A');
let props = Object.getOwnPropertyNames(obj);
for (let i = 0; i < props.length; i++) {
let value = obj[props[i]];
}
performance.mark('B');
performance.measure('GETOWP_FOR', 'A', 'B');
}
console.log(Object.entries(values).sort((a, b) => {
return a[1] - b[1];
}).map(obj => {
obj[1] /= iterations;
return obj;
}));

Results

Rank Technique
Time(ms)
1 Object.keys() with for loop
560.44
2 for...in loop
645.65
3 Object.keys() with forEach
648.31
4 Object.getOwnPropertyNames() with for loop
999.74
5 Object.getOwnPropertyNames() woth forEach
1072.30
6 Object.values() with for loop
1144.67
7 Object.values() with forEach
1116.62
8 Object.entries() with for loop
1880.95
9 Object.entries() with forEach
1980.55

Chart below gives a better comparison overview of the techniques.

Based on above results, the winner or the fastest technique to iterate over Javascript Object entries is Object.keys() with traditional for loop!

Comments

  1. Hello. Thank you for the good content.
    I am a developer in Korea. Can I translate and post this posting?

    ReplyDelete

Post a Comment