139

I want to use a for-each and a counter:

i=0
for blah in blahs
    puts i.to_s + " " + blah
    i+=1
end

Is there a better way to do it?

Note: I don't know if blahs is an array or a hash, but having to do blahs[i] wouldn't make it much sexier. Also I'd like to know how to write i++ in Ruby.


Technically, Matt's and Squeegy's answer came in first, but I'm giving best answer to paradoja so spread around the points a bit on SO. Also his answer had the note about versions, which is still relevant (as long as my Ubuntu 8.04 is using Ruby 1.8.6).


Should've used puts "#{i} #{blah}" which is a lot more succinct.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
  • 1
    There is no increment operator in Ruby. The n+=1 you wrote up there is the closest equivalent. It would have to be syntactic sugar for n+=1, which is itself shorthand for n=n+1. The idea of ++ was rejected because it hides the fact that you're reassigning the variable. http://rubyurl.com/Dsb1 – Chuck Feb 10 '09 at 23:37
  • Nice! Thanks Chuck. I was forced to learn ++i and i++ in Java, so I just thought it would always be there. – Dan Rosenstark Feb 11 '09 at 00:33

8 Answers8

196

As people have said, you can use

each_with_index

but if you want indices with an iterator different to "each" (for example, if you want to map with an index or something like that) you can concatenate enumerators with the each_with_index method, or simply use with_index:

blahs.each_with_index.map { |blah, index| something(blah, index)}

blahs.map.with_index { |blah, index| something(blah, index) }

This is something you can do from ruby 1.8.7 and 1.9.

paradoja
  • 3,055
  • 2
  • 25
  • 34
  • 1
    It's worth noting that this is new in 1.9. – Zach Langley Feb 10 '09 at 20:09
  • 1
    Well spotted. Actually, it's also in 1.8.7, but it's something worth adding. – paradoja Feb 10 '09 at 23:18
  • why am I on Ruby 1.8.6 if I just installed the whole thing on Ubuntu? Is 1.8.7 experimental/beta? – Dan Rosenstark Feb 11 '09 at 00:25
  • Which Ubuntu are you using? In Intrepid there's 1.8.7. It's the last stable version ( http://www.ruby-lang.org/en/downloads/ ). You may, however, monkeypatch ruby 1.8.6 to have the same behavior (see http://www.strictlyuntyped.com/2008/09/ruby-187s-enumerator-class.html ). – paradoja Feb 11 '09 at 22:08
  • Hmmm... I ran sudo apt-get install ruby irb rdoc on my Ubuntu and it got 1.8.6. How should I be doing it? Running Ubuntu 8.04.1, not sure if that's Incredibly Intrepid or Hardly Jeronimo (that joke only works with the second half in Spanish, unfortunately). – Dan Rosenstark Feb 16 '09 at 21:15
  • Ubuntu 8.04 (Hardy :) ) shipped with ruby 1.8.6. Ubuntu's policy is to not update packages (except security issues and things like that) so in the official repos it's the latest. If you want to run ruby 1.8.7 you follow: http://www.vanutsteen.nl/2008/06/29/installing-ruby-187-and-guessnet-on-hardy/ – paradoja Feb 16 '09 at 22:57
  • Argh... "you follow", well, it doesn't matter. Yoy can also install ruby from source, and possibly there are other alternatives for installing the latest ruby in your Ubuntu installation. – paradoja Feb 16 '09 at 22:58
  • 3
    I got tired of the distribution being behind so I started using rvm to manage my Ruby installation on Ubuntu and Linux Mint. – the Tin Man Mar 14 '10 at 07:33
56
[:a, :b, :c].each_with_index do |item, i|
  puts "index: #{i}, item: #{item}"
end

You can't do this with for. I usually like the more declarative call to each personally anyway. Partly because its easy to transition to other forms when you hits the limit of the for syntax.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
11

Yes, it's collection.each to do loops, and then each_with_index to get the index.

You probably ought to read a Ruby book because this is fundamental Ruby and if you don't know it, you're going to be in big trouble (try: http://poignantguide.net/ruby/).

Taken from the Ruby source code:

 hash = Hash.new
 %w(cat dog wombat).each_with_index {|item, index|
   hash[item] = index
 }
 hash   #=> {"cat"=>0, "wombat"=>2, "dog"=>1}
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Matt Rogish
  • 24,435
  • 11
  • 76
  • 92
  • No problem, glad to help. Didn't mean to be "mean" about the .each block -- these are fundamental ruby constructs and learning them on the fly will be really, really painful. :) Best to bite the bullet and spend a few hours reading! – Matt Rogish Feb 11 '09 at 17:48
6

If you don't have the new version of each_with_index, you can use the zip method to pair indexes with elements:

blahs = %w{one two three four five}
puts (1..blahs.length).zip(blahs).map{|pair|'%s %s' % pair}

which produces:

1 one
2 two
3 three
4 four
5 five
George
  • 3,027
  • 1
  • 24
  • 17
  • 1
    Where can I find this code from your answer `'%s %s' % [1, "two"]` in the Ruby docs? Thanks for your help and for conserving vertical space by using one line for zipping and mapping. – Dan Rosenstark May 19 '10 at 01:52
  • 1
    In this context, the %-sign operator is a method of the String class. You can read about it at http://ruby-doc.org/core/classes/String.html#M000770. Or you can type ri String#% at a command prompt. Or you can type ri % to see all of the documented classes that recognize it, e.g., the more familiar meaning at ri Fixnum#%. – George May 19 '10 at 22:11
4

As to your question about doing i++, well, you cannot do that in Ruby. The i += 1 statement you had is exactly how you're supposed to do it.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
pope
  • 628
  • 4
  • 7
3

If you want to get index of ruby for each, then you can use

.each_with_index

Here is an example to show how .each_with_index works:

range = ('a'..'z').to_a
length = range.length - 1
range.each_with_index do |letter, index|
    print letter + " "
    if index == length
        puts "You are at last item"
    end
end

This will print:

a b c d e f g h i j k l m n o p q r s t u v w x y z You are at last item
Ilyas karim
  • 4,592
  • 4
  • 33
  • 47
2

The enumerating enumerable series is pretty nice.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
1

If blahs is a class that mixes in Enumerable, you should be able to do this:

blahs.each_with_index do |blah, i|
  puts("#{i} #{blah}")
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Roy Pardee
  • 196
  • 2
  • 12