Idiomatic way to detect first/last iteration?

Discussion in 'Ruby' started by Kendall Gifford, Jun 11, 2009.

  1. 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.
    Kendall Gifford, Jun 11, 2009
    #1
    1. Advertising

  2. [Note: parts of this message were removed to make it a legal post.]

    On Thu, Jun 11, 2009 at 9:43 AM, 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.
    >
    >

    One option (not necessarily much prettier) would be to explicitly select out
    the portions

    genres = %w{ classical punk rock jazz }
    puts "My absolute favorite #{genres[0]}"
    genres[1..-2].each{ |g| puts "Another favorite #{g}" }
    puts "My least favorite #{genres[-1]}" unless genres.length == 1

    John
    John W Higgins, Jun 11, 2009
    #2
    1. Advertising

  3. On Jun 11, 11:18=A0am, John W Higgins <> wrote:
    >
    > One option (not necessarily much prettier) would be to explicitly select =

    out
    > the portions
    >
    > genres =3D %w{ classical punk rock jazz }
    > puts "My absolute favorite #{genres[0]}"
    > genres[1..-2].each{ |g| puts "Another favorite #{g}" }
    > puts "My least favorite #{genres[-1]}" unless genres.length =3D=3D 1
    >


    This is certainly an option in general. However, for my purposes, I
    should provide more context of my actual situation (instead of my
    contrived example).

    I'm using ruby to build a rails application (I know, now this post may
    become a bit rails specific, but hang with me). Specifically, I'm
    writing code to generate/render the view of one of my pages (using
    erb).

    The gist of it is that I'm outputting a bunch or "rows", 1 for each
    entry in a collection. However, I need the rendering to be slightly
    different for the first and last row. I can totally use
    each_with_index to do this, but I'm hoping for something else. The
    rails stack provides a "helper" lib method called "cycle" that allows
    me to elegantly render rows differently based on some repetition cycle
    (every-other, or every-fifth, etc). The cycle helper allows me to
    avoid using each_with_index and testing the "index % some_val" for row
    coloring. I just want to know if there is a clean, idiomatic way,
    specifically in ruby (not rails library code) to do something similar,
    detecting the first/last iteration of a collection.

    I may just end up writing a rails helper to do something to clean it
    up (and keep my view code clean and low on logic).
    Kendall Gifford, Jun 11, 2009
    #3
  4. I expect there's a better way to do this, but as a quick dash-off....

    x=%w(the big fat cat)

    x.each do |y|
    print "first " if (y==x.first)
    print "last " if (y==x.last)
    puts y
    end

    --
    "... if you do follow your bliss you put yourself on a kind of
    track that has been there all the while, waiting for you, and the life
    that you ought to be living is the one you are living. When you can
    see that, you begin to meet people who are in your field of bliss, and
    they open doors to you. I say, follow your bliss and don't be afraid,
    and doors will open where you didn't know they were going to be." --
    Joseph Campbell
    Matthew K. Williams, Jun 11, 2009
    #4
  5. Kendall Gifford

    Pieter V. Guest

    That won't actually work if your array contains duplicate elements...

    On Thu, Jun 11, 2009 at 10:52 AM, Matthew K. Williams<> wrote:
    > I expect there's a better way to do this, but as a quick dash-off....
    >
    > x=%w(the big fat cat)
    >
    > x.each do |y|
    > print "first " if (y==x.first)
    > print "last " if (y==x.last)
    > puts y
    > end
    >
    > --
    > "... if you do follow your bliss you put yourself on a kind of
    > track that has been there all the while, waiting for you, and the life
    > that you ought to be living is the one you are living. When you can
    > see that, you begin to meet people who are in your field of bliss, and
    > they open doors to you. I say, follow your bliss and don't be afraid,
    > and doors will open where you didn't know they were going to be." --
    > Joseph Campbell
    >
    >
    Pieter V., Jun 11, 2009
    #5
  6. Kendall Gifford

    Brad Ediger Guest

    [Note: parts of this message were removed to make it a legal post.]

    On Thu, Jun 11, 2009 at 12:52 PM, Matthew K. Williams <>wrote:

    > I expect there's a better way to do this, but as a quick dash-off....
    >
    > x=%w(the big fat cat)
    >
    > x.each do |y|
    > print "first " if (y==x.first)
    > print "last " if (y==x.last)
    > puts y
    > end
    >


    Only if all of your elements are unique...

    x=%w(1 2 1 3 4 3)
    Brad Ediger, Jun 11, 2009
    #6
  7. On Fri, 12 Jun 2009, Pieter V. wrote:

    > That won't actually work if your array contains duplicate elements...
    >


    True -- however, given the scenario that the querent provided, it should
    work.....

    Hmmm... could also set a flag, I guess, if you're that opposed to using
    index.

    Matt
    Matthew K. Williams, Jun 11, 2009
    #7
  8. Kendall Gifford

    Gary Wright Guest

    On Jun 11, 2009, at 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?



    The following solution uses each_with_index internally but doesn't
    depend on #first, #last, or #length. On a single item collection
    it will report 'last' but not 'first'. You'll have to decide if
    that works in your case. Obviously you could change it to report
    'both' for a single item.

    Gary Wright


    module Enumerable
    def each_with_position
    previous = sigil = Object.new
    each_with_index { |next_item, index|
    case index
    when 0
    # do nothing
    when 1
    yield(previous, 'first')
    else
    yield(previous, 'middle')
    end
    previous = next_item
    }
    yield(previous, 'last') unless previous == sigil
    end
    end

    >> [1,2,3,4].each_with_position { |*x| p x }

    [1, "first"]
    [2, "middle"]
    [3, "middle"]
    [4, "last"]
    => nil
    >> [].each_with_position { |*x| p x }

    => nil
    >> [1].each_with_position { |*x| p x }

    [1, "last"]
    => nil
    Gary Wright, Jun 11, 2009
    #8
  9. On Jun 11, 2009, at 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?


    I don't see how you can do what you want without some sort of "index".
    In October.2008 I was thinking about how to detect the last iteration
    when you didn't know it was the last iteration, and devised something
    which - unsurprisingly - was very similar to Gary Wright's code.

    (In Gary Wright's code, is there a reason for using Object.new.
    instead of, say, the following minor changes?

    previous = sigil = Object.new ##-
    previous = nil ##+

    yield(previous, 'last') unless previous == sigil ##-
    yield(previous, 'last') if previous ##+ )

    Based on various possibilities for what you may want/need,
    I've made some minor changes to my Enumerable#each_with_index!

    If it is called with no argument, or with nil or false,
    it acts (or should act!) exactly like each_with_index.
    But if it is called with an argument which is not nil or false,
    then it indicates the last iteration by returning index -1,
    (by analogy with -1 meaning the last element of an array, etc),
    unless the last iteration is also the first iteration.
    If the iteration is in the middle, then it returns index > 0.
    If the iteration is the first iteration then it returns index 0,
    *unless* the first iteration will also be the last iteration,
    in which case it returns whatever is the argument you supplied.

    module Enumerable

    # This is similar to each_with index, except that it also tells you
    # whether you are at the end of the enumeration.
    # The index (2nd) item in the yield is an integer >= 0 unless:
    # each_with_index! is called with an argument which is not nil or false,
    # *and* it is the last iteration.
    # In this case the "index" item yielded is -1 *unless* the last iteration
    # is also the first iteration, in which case the "index" value is whatever
    # was the (not nil or false) argument to Enumerable#each_with_index!.
    # This allows you to decide where there is only one iteration
    # whether you want to treat this as being "last" (use argument -1),
    # or "first" (use argument 0), or something else to specifically indicate
    # this is both the first and last iteration.
    #
    # Because of the implementation of this, the "next" object is "available",
    # so there is an option to have that as a 3rd item in the yield.
    # (For example, you could use it as an each "pair" iteration,
    # which you can "break" at index (2nd item in yield) == -1"
    # or, maybe better,at index < 0, depending on the arguments to the method.)
    #
    # If as well as the possibly negative (or something else!) index,
    # you also want an index which doesn't suddenly become negative
    # (or something else!) you can do, for example:
    # kk = -1
    # object.each_with_index!( -1 ) do | obj, ii | kk += 1
    # # code
    # end
    # or
    # kk = nil
    # object.each_with_index!( -1 ) do | obj, ii |
    # if kk then kk += 1 else kk = 0 end
    # # code
    # end
    #
    # (?? Maybe use each_with_index!(*args) to
    # allow more sophisticated arguments ??)
    #
    def each_with_index!( qtype = nil, next_obj = false )
    obj = ii = nil
    self.each do | obje |
    if ii then
    if next_obj then yield obj, ii, obje
    else yield obj, ii
    end
    ii += 1
    else ii = 0
    end
    obj = obje
    end
    if ii then # last, which is also first if ii == 0
    if qtype then
    if ii == 0 then yield obj, qtype
    else yield obj, -1
    end
    else yield obj, ii
    end
    end
    return nil
    end

    end

    ########################### examples ############


    # Applied to your problem, here are some examples.
    # For 3 or more iterations they all behave in the same way.
    # If there are only two iterations, then the 2nd and 3rd examples
    # behave diifferently.
    # If there is only 1 iteration:
    # * the first three display: "my only favorite"
    # * the fourth displays: "my absolute favorite"
    # * the fifth displays: "my least fav. of all my favorites"

    genres = %w{ classical punk rock jazz }
    5.times do | n |
    genres[ -1, 1 ] = [] if n > 0
    puts ; puts "*genres == " + genres.inspect

    puts
    genres.each_with_index!(true) do | genre, ii |
    if ii == 0 then # proper first
    desc = "my absolute favorite"
    elsif ii == true then # first and last
    desc = "my only favorite"
    elsif ii > 0 then # proper middle
    desc = "one of my favorites"
    else # proper last
    desc = "my least fav. of all my favorites"
    end
    puts "#{genre} is #{desc}"
    end

    # or:
    puts
    kk = -1
    genres.each_with_index!(-1) do | genre, ii | kk += 1
    if kk == 0 then # first
    if ii == 0 then # proper first
    desc = "my absolute favorite"
    else # first and last
    desc = "my only favorite"
    end
    elsif ii > 0 then # proper middle
    desc = "one of my favorites"
    else # proper last
    if kk == 1 then
    desc = "my second favorite"
    else
    desc = "my least fav. of all my favorites"
    end
    end
    puts "#{genre} is #{desc}"
    end

    # or the less readable (but faster?):
    puts
    kk = -1
    genres.each_with_index!(-1) do | genre, ii | kk += 1
    if ii > 0 then # proper middle
    desc = "one of my favorites"
    elsif ii == 0 then # proper first
    desc = "my absolute favorite"
    elsif kk == 0 then # first and last
    desc = "my only favorite"
    else # proper last
    if kk == 1 then
    desc = "my second favorite"
    else
    desc = "my least fav. of all my favorites"
    end
    end
    puts "#{genre} is #{desc}"
    end

    # or:
    puts
    genres.each_with_index!(0) do | genre, ii |
    if ii == 0 then # first, and maybe also last
    desc = "my absolute favorite"
    elsif ii > 0 then # proper middle
    desc = "one of my favorites"
    else # proper last
    desc = "my least fav. of all my favorites"
    end
    puts "#{genre} is #{desc}"
    end

    # or (and so on):
    puts
    genres.each_with_index!(-1) do | genre, ii |
    if ii == 0 then # proper first
    desc = "my absolute favorite"
    elsif ii > 0 then # proper middle
    desc = "one of my favorites"
    else # last, and maybe also first
    desc = "my least fav. of all my favorites"
    end
    puts "#{genre} is #{desc}"
    end

    # example of next object as well
    puts
    genres.each_with_index!(-1, true) do | genre, ii, next_obj |
    p [ genre, ii, next_obj ]
    end

    # example of next object as well, but otherwise like each_with_index
    puts
    genres.each_with_index!(nil, true) do | genre, ii, next_obj |
    p [ genre, ii, next_obj ]
    end

    end
    Colin Bartlett, Jun 12, 2009
    #9
  10. Kendall Gifford

    trans Guest

    On Jun 11, 12:43=A0pm, 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 =3D %w{ classical punk rock jazz }
    > genres.each_with_index do |genre, i|
    > =A0 desc =3D "one of my favorites"
    > =A0 desc =3D "my absolute favorite" if i.zero?
    > =A0 desc =3D "my least fav. of all my favorites" if i + 1 =3D=3D genres.l=

    ength
    > =A0 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.

    # =3D Iteration
    #
    class It
    attr_reader :index, :value, :prior, :after
    def initialize(array)
    @array =3D array
    @index =3D 0
    @value =3D array[0]
    @prior =3D []
    @after =3D array[1..-1]
    end
    def first? ; @index =3D=3D 0 ; end
    def last?
    if Enumerable =3D=3D=3D self
    nil
    else
    @index =3D=3D @array.length
    end
    end
    private
    def next_iteration
    @index +=3D 1
    @prior << @value
    @value =3D @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 =3D 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.
    trans, Jun 12, 2009
    #10
  11. 2009/6/11 Gary Wright <>:
    >
    > On Jun 11, 2009, at 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?

    >
    >
    > The following solution uses each_with_index internally but doesn't
    > depend on #first, #last, or #length. On a single item collection
    > it will report 'last' but not 'first'. =A0You'll have to decide if
    > that works in your case. Obviously you could change it to report
    > 'both' for a single item.
    >
    > Gary Wright
    >
    >
    > module Enumerable
    > =A0def each_with_position
    > =A0 =A0previous =3D sigil =3D Object.new
    > =A0 =A0each_with_index { |next_item, index|
    > =A0 =A0 =A0case index
    > =A0 =A0 =A0when 0
    > =A0 =A0 =A0 =A0# do nothing
    > =A0 =A0 =A0when 1
    > =A0 =A0 =A0 =A0yield(previous, 'first')
    > =A0 =A0 =A0else
    > =A0 =A0 =A0 =A0yield(previous, 'middle')
    > =A0 =A0 =A0end
    > =A0 =A0 =A0previous =3D next_item
    > =A0 =A0}
    > =A0 =A0yield(previous, 'last') unless previous =3D=3D sigil
    > =A0end
    > end
    >
    >>> [1,2,3,4].each_with_position { |*x| p x }

    > [1, "first"]
    > [2, "middle"]
    > [3, "middle"]
    > [4, "last"]
    > =3D> nil
    >>> [].each_with_position { |*x| p x }

    > =3D> nil
    >>> [1].each_with_position { |*x| p x }

    > [1, "last"]
    > =3D> nil


    Turns out we think along similar lines, although my solution is a bit
    more wasteful:

    module Enumerable
    # will yield elements along with
    # a symbol :first, :last, :middle or :eek:nly
    def each_special
    last =3D nil

    each do |x|
    if last
    yield *last
    last =3D [x, :middle]
    else
    # first iter
    last =3D [x, :first]
    end
    end

    yield last.first, last.last =3D=3D :middle ? :last : :eek:nly if last
    self
    end
    end

    [
    [],
    [1],
    [1,2],
    [1,2,3],
    [1,2,3,4,5],
    ].each do |en|
    p en

    en.each_special do |x,t|
    printf "%-10p %p\n", t, x
    end

    puts "end"
    end

    Kind regards

    robert
    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
    Robert Klemme, Jun 12, 2009
    #11
  12. Kendall Gifford

    paron Guest

    > I just want to know if there is a clean, idiomatic way,
    > specifically in ruby (not rails library code) to do something similar,
    > detecting the first/last iteration of a collection.


    Is detection really what you want? Doesn't that imply that you test
    each iteration? Since the array is ordered by definition, it seems
    (needs tested!) that creating three entities might be faster.

    in helper:
    def first_also_ran_last(records)

    first_record=records.shift
    last_record=records.pop

    return [first_record, records, last_record]
    end

    in view:
    <% parsed_records = first_also_ran_last(@whatever)
    <%="Really like #{parsed_records[0]}"%>
    <% parsed_records[1].each do |also_ran|%> #(if any)
    <%= "Sorta like #{also_ran}"%>
    <%end -%>
    <%unless parsed_records[2].nil?-%>
    <%= "Don't much like #{parsed_records[2]}"%>
    <%end->

    Don't know if it's faster, or more idiomatic, but perhaps it's more
    semantic?

    Ron
    paron, Jun 12, 2009
    #12
  13. On Fri, Jun 12, 2009 at 1:43 AM, 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.
    >
    >


    This is similar to some other idea already posted but I did not
    understand what you did not like about it.

    If you just want to focus on the first and last, not the third or
    something else, what is wrong with this?

    af,lf = genres.shift, genres.pop
    puts "#{af} is my absolute favorite"
    genres.each {|genre| puts "#{genre} is one of my favorites"}
    puts "#{lf} is my least fav. of all my favorites"


    Harry

    --
    A Look into Japanese Ruby List in English
    http://www.kakueki.com/ruby/list.html
    Harry Kakueki, Jun 12, 2009
    #13
  14. On 12 Jun 2009, at 12:45, paron wrote:
    > Is detection really what you want? Doesn't that imply that you test
    > each iteration? Since the array is ordered by definition, it seems
    > (needs tested!) that creating three entities might be faster.
    >
    > in helper:
    > def first_also_ran_last(records)
    >
    > first_record=records.shift
    > last_record=records.pop
    >
    > return [first_record, records, last_record]
    > end
    >
    > in view:
    > <% parsed_records = first_also_ran_last(@whatever)
    > <%="Really like #{parsed_records[0]}"%>
    > <% parsed_records[1].each do |also_ran|%> #(if any)
    > <%= "Sorta like #{also_ran}"%>
    > <%end -%>
    > <%unless parsed_records[2].nil?-%>
    > <%= "Don't much like #{parsed_records[2]}"%>
    > <%end->
    >
    > Don't know if it's faster, or more idiomatic, but perhaps it's more
    > semantic?


    With a bit of tidying you can reduce the view further:

    def x records, message = "Nothing Relevant" # I can't think of a
    _good_ name for the method off-hand
    first = records.shift || message
    last = records.pop || message
    [first, records.empty? ? [message] || records, last]
    end

    <% track_listing = x records, "I don't have one!!!" %>
    <%= "My favourite track: #{track_listing.shift} %>
    <% track_listing.shift.each do |track| -%>
    <%= "Another track I like: #{track} %>
    <% end -%>
    <%= "The track I like least: #{track_listing.shift} %>


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, Jun 12, 2009
    #14
  15. Kendall Gifford

    Gary Wright Guest

    On Jun 12, 2009, at 1:37 AM, Colin Bartlett wrote:
    > (In Gary Wright's code, is there a reason for using Object.new.
    > instead of, say, the following minor changes?


    > previous = sigil = Object.new ##-
    > previous = nil ##+
    >
    > yield(previous, 'last') unless previous == sigil ##-
    > yield(previous, 'last') if previous ##+ )


    Because you need an object that is guaranteed to not be
    in the enumeration. You can't use nil because that could
    be the last item in the collection, which in my code would
    prevent the last nil from being yielded to the block.

    Gary Wright
    Gary Wright, Jun 12, 2009
    #15
  16. Kendall Gifford

    Gary Wright Guest

    On Jun 12, 2009, at 7:45 AM, paron wrote:
    > first_record=records.shift
    > last_record=records.pop


    You can't use 'shift' and 'pop' if you want the
    solution to work with *any* enumerable object.

    It just depends how general a solution you are
    looking for.
    Gary Wright, Jun 12, 2009
    #16
  17. On Fri, Jun 12, 2009 at 1:43 AM, 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.
    >
    >


    Or how about something like this?

    fav = Hash.new(" is one of my favorites")
    fav[0] = " is my absolute favorite"
    fav[genres.length-1] = " is my least fav. of all my favorites"

    puts genres.zip(Array.new(genres.length){|i| fav}).map{|x| x.join}


    Harry

    --
    A Look into Japanese Ruby List in English
    http://www.kakueki.com/ruby/list.html
    Harry Kakueki, Jun 12, 2009
    #17
  18. Kendall Gifford

    Robert Dober Guest

    On Thu, Jun 11, 2009 at 6: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 =3D %w{ classical punk rock jazz }
    > genres.each_with_index do |genre, i|
    > =A0desc =3D "one of my favorites"
    > =A0desc =3D "my absolute favorite" if i.zero?
    > =A0desc =3D "my least fav. of all my favorites" if i + 1 =3D=3D genres.le=

    ngth
    > =A0puts "#{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.
    >
    >

    Kendall

    streams might give you the abstraction you need

    --------------- 8< ------------------------
    require 'lab419/functional/streams/core'
    require 'lab419/functional/streams/as-enums'

    x =3D %w{a b c a b}.to_stream

    x.each do | s |
    p [ s.head, s.tail =3D=3D EmptyStream ]
    end
    ----------------- >8 ----------------

    I might implement SimpleStream#first?, SimpleStream#last? and
    SimpleStream#middle? if I find some spare time, and if anybody needs
    it ;)

    I admit however that there is still a little price to pay, you might
    have noticed that s is a stream and not an element of the stream, but
    that is just the way streams work.

    HTH
    Robert



    --=20
    Toutes les grandes personnes ont d=92abord =E9t=E9 des enfants, mais peu
    d=92entre elles s=92en souviennent.

    All adults have been children first, but not many remember.

    [Antoine de Saint-Exup=E9ry]
    Robert Dober, Jun 12, 2009
    #18
  19. Kendall Gifford

    paron Guest

    On Jun 12, 8:41 am, Gary Wright <> wrote:
    > On Jun 12, 2009, at 7:45 AM, paron wrote:
    >
    > >  first_record=records.shift
    > >  last_record=records.pop

    >
    > You can't use 'shift' and 'pop' if you want the
    > solution to work with *any* enumerable object.
    >
    > It just depends how general a solution you are
    > looking for.


    Good point; I'd forgotten that case.

    There's really no inherently meaningful "first" or "last" with a Hash,
    so I ignored it. In fact, couldn't you consider "first" and "last"
    meaningless for any Enumerable that isn't indexed? Maybe the question
    shouldn't be about Enumerable.

    Anyway, good catch.

    Ron
    paron, Jun 12, 2009
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. alr
    Replies:
    12
    Views:
    495
  2. Rudi
    Replies:
    5
    Views:
    4,957
  3. Ben Cohen
    Replies:
    4
    Views:
    1,131
    Jean-Michel Pichavant
    May 10, 2010
  4. John Lam
    Replies:
    4
    Views:
    229
  5. Jay Levitt
    Replies:
    19
    Views:
    219
    Steve Austen
    Nov 29, 2010
Loading...

Share This Page