DEV Community

Steve Brownlee
Steve Brownlee

Posted on

Custom Object Iterators in JavaScript

Originally published on stevebrownlee.com

Imagine that you have an object in JavaScript that maintains a list of items, and you want the object itself to be iterable, but the object also has other important properties - such as metadata about the list of items.

Here's a fun and completely original example.

const se7enDwarves = {
    employment: "Miners",
    output: "Diamonds",
    members: new Map()
}
Enter fullscreen mode Exit fullscreen mode
se7enDwarves.members.set(1, "Sleepy")
se7enDwarves.members.set(2, "Grumpy")
se7enDwarves.members.set(3, "Happy")
se7enDwarves.members.set(4, "Bashful")
se7enDwarves.members.set(5, "Sneezy")
se7enDwarves.members.set(6, "Doc")
se7enDwarves.members.set(7, "Dopey")
Enter fullscreen mode Exit fullscreen mode

I now have an object that represents the seven dwarves. It holds their names in an iterable collection, but also other relevent data about them. I would love to be able to iterate over the object itself, instead of having to type in that horrible, troublesome property namespace of members.

I want to see the names of all the dwarves, so I use the spread syntax to iterate the object and place the items in an array.

[...se7enDwarves]

VM2417:1 Uncaught TypeError: se7enDwarves is not iterable
    at <anonymous>:1:5
Enter fullscreen mode Exit fullscreen mode

JavaScript, in all kinds of holy righteousness, tells me that I can't iterate an object like that.

Hmph.

Luckily, JavaScript allows developers to perform what is called metaprogramming. It's a feature of the language that allows us to modify some of the default behaviors of the underlying data structures available to us. I can make an object iterable.

se7enDwarves[Symbol.iterator] = function* () {
  const states = se7enDwarves.members.values()
  let state = null
  do {
    state = states.next().value
    if (typeof state !== "undefined") yield state
  } while (typeof state !== "undefined")
}
Enter fullscreen mode Exit fullscreen mode

High level overview... I made the object iterable. Thought I'd point that out in case it wasn't obvious from that "self-documenting code".

[...se7enDwarves]

(7) ["Sleepy", "Grumpy", "Happy", "Bashful", "Sneezy", "Doc", "Dopey"]
Enter fullscreen mode Exit fullscreen mode

Try it Out on jsfiddle

I create a JSFiddle for you to play around with this code yourself.

Oldest comments (2)

Collapse
 
kepta profile image
Kushan Joshi • Edited

Great article Steve, especially that you found a real world use case of making a custom iterable object. I had similar fun while writing How I learned to Stop Looping and Love the Iterator, though I wish I could add a section on generators.

One suggestion for you to improve the iterator of your se7enDwarves, is that you can redirect iteration to another iterable using yield *:

se7enDwarves[Symbol.iterator] = function* () {
  yield * se7enDwarves.members.values();
}

[...se7enDwarves]

(7) ["Sleepy", "Grumpy", "Happy", "Bashful", "Sneezy", "Doc", "Dopey"]
Collapse
 
stevebrownlee profile image
Steve Brownlee

Thanks, Kushan. I did realize that the iterator could be simplified about a month after I wrote the original article, but never updated it, so thanks for posting what that looks like. Great stuff.