Iterators, Enumerable and Enumerator

15.1 Enumerable Methods (Core Toolkit)

each, each_with_index, each_with_object, each_slice, each_cons, each_entry, cycle, reverse_each

map/collect, flat_map, filter_map, zip, with_index, chunk_while, slice_when, chunk

select/filter, reject, grep, grep_v, partition, take_while, drop_while, uniq, compact

find/detect, find_index, first, min, max, min_by, max_by, minmax

reduce/inject, sum, count, tally, group_by, to_h, to_set

all?, any?, none?, one?, include?/member? sort, sort_by

orders = [
  { id: 1, customer: "Ana", total: 120, status: :paid },
  { id: 2, customer: "Bo", total: 80, status: :pending },
  { id: 3, customer: "Ana", total: 200, status: :paid }
]

orders.select { |o| o[:status] == :paid }.sum { |o| o[:total] }       # 320
orders.group_by { |o| o[:customer] }.transform_values { |os| os.sum { it[:total] } }
orders.max_by { |o| o[:total] }[:id]
orders.map { |o| o[:customer] }.tally                                 # {"Ana"=>2, "Bo"=>1}
orders.each_with_object(Hash.new(0)) { |o, h| h[o[:status]] += 1 }
orders.partition { |o| o[:total] > 100 }
orders.sort_by { |o| [-o[:total], o[:id]] }
orders.sum { _1[:total] } / orders.size.to_f
[1, 2, 4, 9, 10, 11, 15].slice_when { |a, b| b != a + 1 }.to_a   # [[1, 2], [4], [9, 10, 11], [15]]
%w[apple avocado banana].chunk { |w| w[0] }.to_a
(1..20).grep(5..8)
%w[a1 b2 c].grep(/\d/)

15.2 Making Your Class Enumerable

class Playlist
  include Enumerable

  def initialize(*songs) = @songs = songs

  def each(&block)
    return enum_for(:each) unless block_given?
    @songs.each(&block)
    self
  end
end

playlist = Playlist.new("A", "B", "C")
playlist.map(&:downcase); playlist.sort; playlist.include?("B"); playlist.first(2)

15.3 Enumerators

enum = [1, 2, 3].each           # external enumerator
enum.next; enum.next; enum.peek
enum.rewind

fib = Enumerator.new do |y|     # generator
  a, b = 0, 1
  loop do
    y << a
    a, b = b, a + b
  end
end
fib.take(10)
fib.lazy.select(&:even?).first(5)

%w[a b c].each.with_index(1).map { |s, i| "#{i}. #{s}" }
[4, 5].map.with_index { |n, i| n * i }
Enumerator.produce(1) { |x| x * 2 }.take(5)     # [1, 2, 4, 8, 16]

15.4 Lazy Enumerators

(1..Float::INFINITY).lazy
  .map { |n| n * n }
  .select(&:even?)
  .first(5)                     # [4, 16, 36, 64, 100]

File.foreach("huge.log").lazy.grep(/ERROR/).map(&:strip).first(10)   # memory-efficient
(1..).lazy.filter_map { |n| n * 2 if n % 3 == 0 }.take(3).to_a