javascript generators for conditional lists

2020-10-23

 | 

~4 min read

 | 

619 words

Generators are a language feature that I don’t see used very commonly - at least in Javascript land (I remember going to a php user group meetup and folks were swearing by their use). In fact, I remember the first time I saw them and how confused I was! I recently came across what I think is really nice use case for them: list creation where the items of the list should be included based on conditions.

For the purposes of this demonstration, I’ll be starting with a contrived example where the conditions are all based on the presence of elements within an object. More than that, we’ll be returning the properties themselves to start. However, as I’ll show later the yielded values can be totally unrelated or even have conditions related to an application state or some other details that the generator has access to from its scope.

First, let’s do it the “hard” way - i.e. the way I would do it up until today (and likely even after today most of the time):

arrayList.js
const list = []
const order = {
  price: 1000,
  style: "fancy",
  product: "shoe",
  brand: "ysl",
}
function populateList(list, order) {
  if (order.price > 500) {
    list.push(order.price)
  }
  if (order.style === "normal") {
    list.push(order.style)
  }
  if (order.product === "shoe") {
    list.push(order.product)
  }
  if (order.brand) {
    list.push(order.brand)
  }
  return list
}
populateList(list, order)
console.log(list) // [1000, 'shoe','ysl']

This works, but it’s rather clunky and relies on scoping, manipulating an array, and generally tracking a lot of details. If this gets really complicated, I can see how the mental overhead could be a lot.

So, what’s the alternative? A generator could work! Let’s see how:

generatedList.js
const order = {
  price: 1000,
  style: "fancy",
  product: "shoe",
  brand: "ysl",
}
function* generateList(order) {
  if (order.price > 500) {
    yield order.price
  }
  if (order.style === "normal") {
    yield order.style
  }
  if (order.product === "shoe") {
    yield order.product
  }
  if (order.brand) {
    yield order.brand
  }
}
const list = Array.from(generateList(order))
console.log(list) // [1000, 'shoe','ysl']

Gone is the management of the array. Gone is the return statements. Instead we’re left with yield statements.

The interesting thing is that each time you call a generator, it proceeds until a yield is reached at which point it stops.

A generator returns an Iterable. Iterables can call next() and will return the next value until the iterator is done. In the example above, we iterate all the way through for free because of the Array.from() call.

This example may be too contrived to be useful. Let’s try a slightly more robust example now that we understand how the generators work a little better:

generateActions.js
const order = {
    status: 'NEEDS_ACTION'
    shipping: {
        status: 'PACKAGED',
        ship_time: Date.now(),
    }
    price: 1000,
    style: 'fancy',
    product: 'shoe',
    brand: 'ysl'
}
function *generateActions(order){
    if(order?.shipping?.status === 'NOT YET SHIPPED' || order?.shipping?.ship_time < Date.now()){
        yield 'early_ship'
    }
    if(order?.shipping?.status === 'SHIPPED'){
        yield 'recall'
    }
    if(order?.status === 'NEEDS_ACTION'){
        yield 'troubleshoot'
    }

}
const list = Array.from(generateActions(order))
console.log(list) // ['troubleshoot']

In this case, based on the order, we derive a series of actions. Again, this can be done with the Array.push approach. The generator is just slightly cleaner about what’s actually happening. I’m excited to continue to explore the different ways to use generators.

For example, imagine a collection that needs different actions available based on the current state. If the item needs action, yield one item, If the item has been canceled, yield a different action, etc. In that situation, the actions are not actually derived from the item (as I was doing above passing along the different properties of price, style, product, and brand), but the item determines the conditions under which actions are made available.



Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!