Я создал небольшое расширение утилиты, чтобы справиться с этой ситуацией. Расширение немного уродливо, но оно делает код более понятным везде.
Позволяет написать код, подобный:
nodes.each_position do |position|
position.first do |first_node|
# Do stuff on just the first node
end
position.all do |node|
# Do stuff on all the nodes
end
position.last do |last_node|
# Do some extra stuff on the last node
end
end
Добавьте это куда-нибудь:
#
# Extends enumerable to give us a function each_index
# This returns an Index class which will test if we are the first or last
# or so item in the list
# Allows us to perform operations on only selected positions of an enumerable.
#
module Enumerable
class Index
attr_accessor :index
attr_accessor :object
def initialize(count)
@index = 0
@count = count
@object = nil
end
def first
if @index == 0
yield(@object)
end
end
def not_first
if @index != 0
yield(@object)
end
end
def last
if @index == @count - 1
yield(@object)
end
end
def not_last
if @index != @count - 1
yield(@object)
end
end
def all
yield(@object)
end
def index(idx)
if @index == idx
yield(@object)
end
end
end
# Add the method to enumerables.
# Iterates through the list. For each element it creates an Index instance
# and passes it the index of the iteration, the length of our list and the
# object at the index.
#
def each_position
count = 0
index = Index.new(self.length)
self.each do |obj|
index.index = count
index.object = obj
yield index
count += 1
end
end
end