tags:

views:

3218

answers:

7

I know you Ruby people will laugh at my bad Ruby code:

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

I want to use a for-each and a counter... 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.

Edit: 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).

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

+5  A: 

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}
Matt Rogish
Thanks for the link (excellent) and the answer.
Yar
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
No worries, thanks for your answer, @Matt.
Yar
+14  A: 
[: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.

Squeegy
Nice, I see the point about the limits with the for syntax. I have to get used to blocks with multiple parameters.
Yar
+1  A: 

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

Roy Pardee
+5  A: 

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:

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

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

paradoja
It's worth noting that this is new in 1.9.
Zach Langley
Well spotted. Actually, it's also in 1.8.7, but it's something worth adding.
paradoja
why am I on Ruby 1.8.6 if I just installed the whole thing on Ubuntu? Is 1.8.7 experimental/beta?
Yar
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
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).
Yar
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
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
I got tired of the distribution being behind so I started using rvm to manage my Ruby installation on Ubuntu and Linux Mint.
Greg
+1  A: 

The enumerating enumerable series is pretty nice.

Andrew Grimm
Nice, thanks for that and good luck on SO.
Yar
+1  A: 

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.

pope
Cool, thanks, that was covered in the comments on the question...
Yar
+1  A: 

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
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.
Yar
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