tags:

views:

139

answers:

4

Let's say I have a loop like this:

items.each do |x|
  if FIRST_TIME_AROUND
    # do something
  end
  # do the rest of stuff
end

is there a way in Ruby to write if FIRST_TIME_AROUND? I vaguely recall reading something about this once, but I can't recall.

EDIT: I do know about the (many) standard ways of doing this... I'm after the most elegant solution possible.

A: 

There's an ugly, generic way, not specific to Ruby:

first = true
items.each do |x|
  if first
    first = false
    # do something
  end
  # do the rest of stuff
end

That kind of logic is ugly, verbose, but works in most languages.

Yoav Shapira
+8  A: 
items.each_with_index do |x, i|
  do_something if i==0
  do_rest
end
ykaganovich
you can toss that i as the second variable and it's the current number in the loop starting with 0. I typically call it int, but i works just fine.
rpflo
+3  A: 

The most elegant way is to do the once-off thing outside the loop if at all possible.

A: 

I created a little utility extension to handle this situation. The extension is a little ugly, but it does make for neater code everywhere else.

It allows you to write code like :

    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

Add this somewhere :

#
# 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
Mongus Pong