назвал его classify
по аналогии с методом из класса Set
.
module Enumerable
def classify(&block)
hash = {}
self.each do |x|
result = block.call(x)
(hashfresult] ||= []) << x
end
hash
end
end
nums = [1,2,3,4,5,6,7,8,9]
mod3 = nums.classify {|x| x % 3 }
# { 0=>[3,6,9], 1=>[1,4,7], 2=>[2,5,8] }
words = %w( area arboreal brick estrous clear donor ether filial
patina ]
vowels = words.classify {|x| x.count('aeiou') }
# {1=>['brick'], 2=>['clear', 'donor', 'ether'],
# 3=>['area', 'estrous', 'filial', 'patina'], 4=>['arboreal']}
initials = words.classify {|x| x[0..0] }
# {'a'=>['area', 'arboreal'], 'b'=>['brick'], 'c'=>['clear'],
# 'd'=>['donor'], 'p'=>['patina'], 'e'=>['estrous', 'ether'],
# 'f'=>['filial']}
8.3.4. Обход с группировкой
До сих пор мы обходили список по одному элементу за раз. Но иногда желательно на каждой итерации анализировать по два, три или более элементов.
Итератор each_slice
принимает в качестве параметра число n, равное числу просматриваемых на каждой итерации элементов. (Для работы с ним нужна библиотека enumerator
.) Если не осталось достаточного количества элементов, размер последнего фрагмента будет меньше.
require 'enumerator'
arr = [1,2,3,4,5,6,7,8,9,10]
arr.each_slice(3) do |triple|
puts triple.join(',')
end
# Выводится:
# 1,2,3
# 4,5,6
# 7,8,9
# 10
Имеется также итератор each_cons
, который позволяет обходить набор методом «скользящего окна» заданного размера. (Если название кажется вам странным, знайте, что это наследие языка Lisp.) В таком случае фрагменты всегда будут иметь одинаковый размер.
require 'enumerator'
arr = [1,2,3,4,5,6,7,8,9,10]
arr.each_cons(3) do |triple|
puts triple.join(',')
end
# Выводится:
# 1,2,3
# 2,3,4
# 3,4,5
# 4,5,6
# 5,6,7
# 6,7,8
# 7,8,9
# 8,9,10
8.3.5. Преобразование в массив или множество
Каждая перечисляемая структура теоретически может быть тривиально преобразована в массив (методом to_a
). Например, такое преобразование для хэша дает вложенный массив пар:
hash = {1=>2, 3=>4, 5=>6}
arr = hash.to_a # [[5, 6], [1, 2], [3, 4]]
Синонимом to_a
является метод entries
.
Если была затребована библиотека set
, становится доступен также метод to_set
. Дополнительная информация о множествах приведена в разделе 9.1.
require 'set'
hash = {1=>2, 3=>4, 5=>6}
set = hash.to_set # #<Set: {[1, 2], [3, 4], [5, 6]}>
8.3.6. Энумераторы
Объект класса Enumerator
— по существу, обертка, превращающая итераторный метод в полноценный объект Enumerable
. Обернутый таким способом итератор приобретает все методы и свойства, присущие перечисляемым структурам.
В следующем искусственном примере в классе Foo
есть итератор и больше ничего. Да и сам-то итератор не делает ничего полезного, только четыре раза вызывает yield
. Чтобы подчеркнуть особенность его работы, итератор назван every
, а не each
.
require 'enumerator'
class Foo
def every