From: trans Date: 2009-06-12T19:30:23+09:00 Subject: Re: Idiomatic way to detect first/last iteration? On Jun 11, 12:43 pm, Kendall Gifford wrote: > Hi there, is there any recognized ruby idiomatic way for detecting > when you're on the first and/or last iteration over a collection? Or, > if not, anyone know of any tricky ways? > > Specifically, I'm trying to avoid using each_with_index (as shown > below): > > genres = %w{ classical punk rock jazz } > genres.each_with_index do |genre, i| >   desc = "one of my favorites" >   desc = "my absolute favorite" if i.zero? >   desc = "my least fav. of all my favorites" if i + 1 == genres.length >   puts "#{genre} is #{desc}" > end > > I just don't like having to compare an index value (especially as when > detecting the final iteration) and hate even having it as a loop > parameter unless absolutely necessary. I know, I'm picky. Just wanted > to beat the bushes and see if any cool alternatives pop up. I've experimented with something along these lines. # = Iteration # class It attr_reader :index, :value, :prior, :after def initialize(array) @array = array @index = 0 @value = array[0] @prior = [] @after = array[1..-1] end def first? ; @index == 0 ; end def last? if Enumerable === self nil else @index == @array.length end end private def next_iteration @index += 1 @prior << @value @value = @after.shift end end class Array # Iterate over each element of array using an iteration object. # # [1,2,3].each_iteration do |it| # p it.index # p it.value # p it.first? # p it.last? # p it.prior # p it.after # end # # on each successive iteration produces: # # 0 1 2 # 1 2 3 # true false false # false false true # [] [1] [1,2] # [2,3] [3] [] # # CREDIT: Trans def each_iteration if block_given? it = It.new(self) each do |e| yield(it) it.send(:next_iteration) end else return Enumerable::Enumerator.new(self, :each_iteration) end end end T.