Friday, 27 April 2018

How do I loop through or enumerate a JavaScript object?

It's interesting people in these answers have touched on both Object.keys() and for...of but never combined them:


var map = {well:'hello', there:'!'};
for (let key of Object.keys(map))
console.log(key + ':' + map[key]);

You can't just for...of an Object because it's not an iterator, and for...index or .forEach()ing the Object.keys() is ugly/inefficient.
I'm glad most people are refraining from for...in (with or without checking .hasOwnProperty()) as that's also a bit messy, so other than my answer above, I'm here to say...




You can make ordinary object associations iterate! Behaving just like Maps with direct use of the fancy for...of
DEMO working in Chrome and FF (I assume ES6 only)


var ordinaryObject = {well:'hello', there:'!'};
for (let pair of ordinaryObject)
//key:value
console.log(pair[0] + ':' + pair[1]);
//or
for (let [key, value] of ordinaryObject)
console.log(key + ':' + value);

So long as you include my shim below:


//makes all objects iterable just like Maps!!! YAY
//iterates over Object.keys() (which already ignores prototype chain for us)
Object.prototype[Symbol.iterator] = function() {
var keys = Object.keys(this)[Symbol.iterator]();
var obj = this;
var output;
return {next:function() {
if (!(output = keys.next()).done)
output.value = [output.value, obj[output.value]];
return output;
}};
};

Without having to create a real Map object that doesn't have the nice syntactic sugar.


var trueMap = new Map([['well', 'hello'], ['there', '!']]);
for (let pair of trueMap)
console.log(pair[0] + ':' + pair[1]);

In fact, with this shim, if you still wanted to take advantage of Map's other functionality (without shimming them all in) but still wanted to use the neat object notation, since objects are now iterable you can now just make a Map from it!


//shown in demo
var realMap = new Map({well:'hello', there:'!'});



For those who don't like to shim, or mess with prototype in general, feel free to make the function on window instead, calling it something like getObjIterator() then;


//no prototype manipulation
function getObjIterator(obj) {
//create a dummy object instead of adding functionality to all objects
var iterator = new Object();
//give it what the shim does but as its own local property
iterator[Symbol.iterator] = function() {
var keys = Object.keys(obj)[Symbol.iterator]();
var output;
return {next:function() {
if (!(output = keys.next()).done)
output.value = [output.value, obj[output.value]];
return output;
}};
};
return iterator;
}

Now you can just call it as an ordinary function, nothing else is affected


var realMap = new Map(getObjIterator({well:'hello', there:'!'}))

or


for (let pair of getObjIterator(ordinaryObject))

There's no reason why that wouldn't work.


Welcome to the future.

No comments:

Post a Comment

casting - Why wasn't Tobey Maguire in The Amazing Spider-Man? - Movies & TV

In the Spider-Man franchise, Tobey Maguire is an outstanding performer as a Spider-Man and also reprised his role in the sequels Spider-Man...