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