Lazy Evaluation Pipeline

JavaScript

class Pipe {
  constructor(sequence) {
    this.sequence = sequence[Symbol.iterator]()
  }

  [Symbol.iterator]() {
    return this.sequence;
  }

  where(predicate) {
    const sequence = this.sequence
    return step(function*() {
      for (const item of sequence) {
        if (predicate(item)) yield item
      }
    })
  }

  take(count) {
    const sequence = this.sequence
    return step(function*() {
      let index = 0;
      while (index < count) {
        yield sequence.next().value
        index++
      }
    })
  }

  all() {
    return Array.from(this.sequence)
  }
}

const pipe = (sequence) => new Pipe(sequence)

const step = (generator) => new Pipe({[Symbol.iterator]: generator})

const books = [
  { title: "Anger Of Silver", rating: 5 },
  { title: "Rat Of Freedom", rating: 3 },
  { title: "Savior Of Dread", rating: 5 },
  { title: "Thieves Of Heaven", rating: 4 },
  { title: "Agents Of Hope", rating: 3 },
  { title: "Edge Of Insanity", rating: 5},
  { title: "Blacksmiths And Friends", rating: 2 },
  { title: "Vultures And Warriors", rating: 4 },
  { title: "Intention Of Freedom", rating: 3 },
  { title: "Searching At The City", rating: 5 },
  { title: "Separated In The Mountains", rating: 2 },
  { title: "Family Of The Banished", rating: 5 },
  { title: "Blinded In My Destiny", rating: 4 },
  { title: "Memory Of The Depths", rating: 3 }
]

const byRating = (b) => {
  console.log("where(byRating) called")
  return b.rating == 5
}

const result = pipe(books).where(byRating).take(3)

console.log(result.all())