How can a Javascript object become iterable with for...of statement? [duplicate]
Asked Answered
S

3

20

I would like to set the options[Symbol.iterator] property in order to iterate on the simple objects I create with the for...of statement :

options = {
  male: 'John',
  female: 'Gina',
  rel: 'Love'
};


for(let p of options){
  console.log(`Property ${p}`);
};

But this code gives me the following error:

 array.html:72 Uncaught TypeError: options[Symbol.iterator] is not a function

How I can set the right iterator function on a simple object as above?

Solved

 // define the Iterator for the options object 
 options[Symbol.iterator] = function(){

     // get the properties of the object 
     let properties = Object.keys(this);
     let count = 0;
     // set to true when the loop is done 
     isDone = false;

     // define the next method, need for iterator 
     let next = () => {
        // control on last property reach 
        if(count >= properties.length){
           isDone = true;
        }
        return {done:isDone, value: this[properties[count++]]};
     }

     // return the next method used to iterate 
     return {next};
  };

And I can use the for...of statement on my object now iterable :

 for(let property of options){
   console.log(`Properties -> ${property}`);
 }
Shudder answered 5/3, 2016 at 20:50 Comment(2)
To use a for...of loop, the collection would have to have a [Symbol.iterator] property. Object literals do not have such a property, only arrays, Sets, maps etc doTrisa
You want to use a Map for that.Overunder
H
32

To use for...of loop you should define an appropriate iterator for your object using [Symbol.iterator] key.

Here is one possible implementation:

let options = {
  male: 'John',
  female: 'Gina',
  rel: 'Love',
  [Symbol.iterator]: function * () {
    for (let key in this) {
      yield [key, this[key]] // yield [key, value] pair
    }
  }
}

Though, in most cases it'll be better to iterate over objects using plain for...in loop instead.

Alternatively you could convert your object to iterable array using Object.keys, Object.values or Object.entries (ES7).

Hilburn answered 5/3, 2016 at 21:4 Comment(7)
You really should just do [Symbol.iterator]() { return Object.entries(this) } (apart from the fact that there is no Object.entries in ES6)Overunder
@Overunder no, it won't work. You have to return an iterator, not an iterable Object. To re-use iterator from Object.entries you should return Object.entries(this)[Symbol.iterator]().Hilburn
Ouch, I totally forgot that Object.entries is a ES7 draft and not a part of ES6.Hilburn
Doesn't entries already return an iterator? But maybe I'm wrong, or there are multiple drafts.Overunder
@Overunder noop, Object.entries returns a plain JS array of [key, value] pairs.Hilburn
It should only return this[key]. from spec: for (variable of iterable) {statement} developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Guile
This is the way I implemented it: for (let value of Object.values(this)) yield valueGuile
Z
13

If you dont want to use generator syntax, you can use another way define iterator function.

    var options = {
        male: 'John',
        female: 'Gina',
        rel: 'Love',
        [Symbol.iterator]: function () {
            var self = this;
            var values = Object.keys(this);
            var i = 0;
            return {
                next: function () {
                    return {
                        value: self[values[i++]],
                        done: i >= values.length
                    }
                }
            }
        }
    };

    for (var p of options) {
        console.log(`Property ${p}`);
    }
Zeuxis answered 5/3, 2016 at 21:33 Comment(1)
I like this answer because unlike the generator method, this option allows you to return an object with whatever keys you want (as oppossed to just returning 'value' and 'done') For instance, you could return an object with 'key':values[i++].Lashaun
Q
6

Plain objects (options in this case) are not iterable in ES6. You need to either define an iterator for your object or do something like:

for(let k of Object.keys(options)) {
  console.log(`Property ${k}, ${options[k]}`);
};
Quinacrine answered 5/3, 2016 at 21:9 Comment(4)
There is no Object.entries in ES6Overunder
@Bergi, thanks for that! Glad to know that they are bring it back in ES7.Quinacrine
@hallucinations: They are not going to be part of ES7 either. Most likely ES8.Hulbard
@FelixKling I saw this proposal and thought they were!Quinacrine

© 2022 - 2024 — McMap. All rights reserved.