Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set key in a Javascript generator function

Hi I'm trying to figure out how to set up the key in a symbol iterator, this is the code I have so far:

let james = {
    name: 'James',
    height: `5'10"`,
    weight: 185
};

james[Symbol.iterator] = function* () {
    for (let key in this) {
        yield this[key];
    }
}

let iterator = james[Symbol.iterator]();
console.log(iterator.next().value); // 'James'
console.log(iterator.next().value); // `5'10`
console.log(iterator.next().value); // 185

the issue I'm having is for example: the call of iterator.next() should print

{"value": "James", "key": "name", "done": false}

but I'm getting

{"value": "James", "done": false}

I'd like to set somehow the "key" as the same way I'm setting up the "value".

I checked to the documentation, but I haven't seen any docs related to this.

Any ideas?

EDIT

the use case for the question was basically turning the James object into an iterable object, it doesn't matter "how", so my first try was to use a generator, then I realized I needed to print the object in this format:

{ value: 'James', key: 'name', done: false }
{ value: '5\'10"', key: 'height', done: false }
{ value: 185, key: 'weight', done: true }

which is not a standard way to do it, so I had to create a custom method to "implement" the behavior:

thanks to @loganfsmyth for pointing me in the right direction, I came up with this simple solution:

let james = {
    name: 'James',
    height: `5'10"`,
    weight: 185
};

james[Symbol.iterator] = function (){
   const keys = [];
   for (let key in this) {
      keys.push({'key':key, 'value':this[key]});
    }
  return {
    next(){
      let {key,value} = keys.shift();
      return {value,key,done:keys.length===0};
    }
  }
}

let iterator = james[Symbol.iterator]();
console.log(iterator.next().value); // 'James'
console.log(iterator.next().value); // `5'10`
console.log(iterator.next().value); // 185
like image 398
pedrommuller Avatar asked Oct 19 '25 12:10

pedrommuller


1 Answers

If you want to yield both the key and the value, you'd need to yield an array with two items, e.g.

yield [key, this[key]];

then the .value on the object would be a key/value pair.

Also note that this behavior is similar to what you get from Object.entries(james) which is a new function added in ES2017, except that it returns an Array.

If explicitly want

.next()

to return an object with both key, value and done, then the answer is you can't do that with a generator. That's not the API that generators provide, and it's not the iterator protocol defined in ES6, so you shouldn't be using an iterator to try to do this.

You could certainly do

james.makeCustomThing = function(){ 
  const pairs = [];
  for (const key in this) pairs.push([key, this[key]]);
  return {
    next(){
      if (pairs.length === 0) return {done: true};

      let [key, value} = pairs.shift();
      return {key, value, done: false};
    },
  };
};

but then it's not an iterator anymore, it's just a custom thing you've defined yourself.

like image 194
loganfsmyth Avatar answered Oct 21 '25 01:10

loganfsmyth