Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

Ruby Enumerable and string

Mike Gehard
Friday, August 27, 2010

Did you know that you can call map() and each() on a Ruby string? Do you know how they behave? I hope I’m not the only one that thought they understood it but was proven mistaken.

What would you expect the following code to do?

"hello".map{|char| "Char#{char}"  }

I thought it would return me an array with a bunch of “Char” where x is each letter in “hello”. Nope…it returns:

["Charhello"]

What would you expect the following code to do?

"hello".each{|char| puts "Char#{char}"}

Yeah I thought it would write out a bunch of puts statements “Char” where x is each letter in “hello”. Nope it prints:

Charhello

Ok so maybe my thoughts on strings being enumerable were a little off…I’ve been wrong before and will probably be wrong again.

What really threw me for a loop was that I can access the characters in a string by index:

"hello"[1]

returns

101

which is the ASCII representation of “e”.

So what is the moral of this story? Sometimes things aren’t as the seem at first glance. And sometimes you need to step back, fire up irb and see what really is happening.

Update: Thanks all for the responses below.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

13 Comments

  1. Matthew Closson says:

    irb(main):008:0> String.instance_methods.each.select {|m| m if m.to_s.include?(‘each’)}
    => [:each_line, :each_byte, :each_char, :each_codepoint]

    irb(main):009:0> “test”.each {|c| puts c}
    NoMethodError: undefined method `each’ for “test”:String
    from (irb):9
    from C:/Ruby19/bin/irb:12:in `

    ‘

    irb(main):010:0> “test”.map {|c| c}
    NoMethodError: undefined method `map’ for “test”:String
    from (irb):10
    from C:/Ruby19/bin/irb:12:in `

    ‘

    What version of Ruby is this on? Also is it possible that you’ve got another library which has monkey patched MethodMissing in the String class? Is this in a straight irb session or maybe from a ./script/console or rails console session? I think .each_byte or .each_char would do what you’re looking for but probably there is some kind of 3rd party intervention going on here. Good luck!

    August 27, 2010 at 7:57 am

  2. Mike Gehard says:

    Here’s an example console output:

    xxxx:~ xxxx$ gem list

    *** LOCAL GEMS ***

    rake (0.8.7)
    rdoc (2.5.9)
    xxxx:~ xxxx$ irb
    1.9.2-p0 > “hello”.each{|char| puts “Char#{char}”}
    Charhello
    => “hello”
    1.9.2-p0 >

    Also, the Ruby documentation says that [String](http://ruby-doc.org/core/classes/String.html) includes the Enumerable module so you’d expect to get each() and map(). Interesting that your version doesn’t behave the same way.

    August 27, 2010 at 8:22 am

  3. JGeiger says:

    He’s using 1.8.x, you’re using 1.9.x

    String does a lot of different things when going to 1.9.x.

    August 27, 2010 at 8:22 am

  4. JGeiger says:

    $ irb
    ruby-1.9.2-p0 > “hello”.each{|char| puts “Char#{char}”}
    NoMethodError: undefined method `each’ for “hello”:String
    from (irb):1
    from /Users/jgeiger/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `

    ‘
    ruby-1.9.2-p0 >

    Dunno why your ruby is doing that, but 1.9 doesn’t include each.

    http://blog.grayproductions.net/articles/ruby_19s_string

    “In Ruby 1.8, String’s each() method iterated over lines of data. I imagine that was done because it’s a common way to process data, but the question is what made lines the correct choice? What about iterating by bytes or characters? You could iterate by bytes in Ruby 1.8 using each_byte(), but you needed to resort to Regexp tricks to get characters.

    In the Ruby 1.9 realm of all encoded data, blessing one type of iteration just doesn’t make sense. Instead, each() has been removed from String and it is no longer Enumerable. This is probably one of the biggest changes to the core API that code will need to adapt to.”

    August 27, 2010 at 8:26 am

  5. Mike Gehard says:

    Looks like 1.8.7-p299 behaves the same way:

    info: Using ruby 1.8.7 p299
    xxxx:~ xxxx$ gem list

    *** LOCAL GEMS ***

    rake (0.8.7)
    rdoc (2.5.9)
    xxxx:~ xxxx$ irb
    ruby-1.8.7-p299 > “hello”.each{|char| puts “Char#{char}”}
    Charhello
    => “hello”
    ruby-1.8.7-p299 >

    August 27, 2010 at 8:26 am

  6. Mike Gehard says:

    Very interesting indeed…

    JGeiger…it is very interesting that my 1.9.2-p0 seems to include each(). Thanks for the link. I’ll do a little more poking around and see what I find…

    August 27, 2010 at 8:32 am

  7. Matthew Closson says:

    C:>ruby -v
    ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-mingw32]

    C:>ruby -e “p String.included_modules”
    [Comparable, Kernel]

    I’ll try on 1.9.2 on a Linux box in a bit and post the results.

    August 27, 2010 at 8:59 am

  8. Prakash Murthy says:

    Got to know through RubyKoans that Ruby 1.8 and 1.9 differ in the way how single characters in strings are represented. Here is the excerpt from koans/about_strings.rb

    ====================================
    in_ruby_version(“1.8″) do
    def test_in_ruby_1_8_single_characters_are_represented_by_integers
    assert_equal __, ?a
    assert_equal __, ?a == 97

    assert_equal __, ?b == (?a + 1)
    end
    end

    in_ruby_version(“1.9″) do
    def test_in_ruby_1_9_single_characters_are_represented_by_strings
    assert_equal , ?a
    assert_equal __, ?a == 97
    end
    end
    ====================================

    Found this page with differences between 1.8 and 1.9:
    http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l112

    String: No longer an Enumerable

    String is not Enumerable anymore. Use #each_line instead of #each, and #lines (see below) to iterate over the lines.

    August 27, 2010 at 9:54 am

  9. Mike Gehard says:

    Not sure what was up this morning with my install of 1.9.2-p0 but now I get the following:

    ruby-1.9.2-p0 > ‘this thing’.each{|a| puts a}
    NoMethodError: undefined method `each’ for “this thing”:String
    from (irb):1
    from /Users/pivotal/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `

    ‘
    ruby-1.9.2-p0 > ‘this’.is_a? Enumerable
    => false

    Maybe my machine was already checked out for the weekend already.

    Final verdict is that in 1.8.x, each() on a String breaks on a line and in 1.9.x, String is no longer Enumerable so it is no longer an issue.

    Thanks all for the comments and for keeping tabs on Pivotal Blabs.

    August 27, 2010 at 10:03 am

  10. Prakash Murthy says:

    The code from RubyKoans – with MarkDown

    in_ruby_version(“1.8″) do
    def test_in_ruby_1_8_single_characters_are_represented_by_integers
    assert_equal 97, ?a
    assert_equal true, ?a == 97

    assert_equal true, ?b == (?a + 1)
    end
    end

    in_ruby_version(“1.9″) do
    def test_in_ruby_1_9_single_characters_are_represented_by_strings
    assert_equal a, ?a
    assert_equal __, ?a == 97
    end
    end

    August 27, 2010 at 10:04 am

  11. pmacek says:

    Matthew Closson, I liked your post but I wanted to give it a little poke.

    You can drop the `m if `from your statement and write `String.instance_methods.each.select {|m| m.to_s.include?(‘each’)}`

    `Array#select` returns an array of elements where the block evaluates to true :)

    August 27, 2010 at 4:19 pm

  12. pete says:

    @Matthew Closson, pmacek:

    Even shorter…

    > String.instance_methods.grep /each/
    => [:each_line, :each_byte, :each_char, :each_codepoint]

    August 28, 2010 at 12:46 am

  13. Matthew Closson says:

    pmacek & pete — nice calls on both of those statement improvements.

    Mike — glad to see that its figured out now, and look forward to future blog posts.

    August 29, 2010 at 11:34 am

Add New Comment Cancel reply

Your email address will not be published.

Mike Gehard

Mike Gehard
Boulder

Recent Posts

  • Using Jasmine to test CoffeeScript in a Rails 3.1 App
  • Waiting for jQuery Ajax calls to finish in Cucumber
  • Mocking Fog when using it with Carrierwave
Subscribe to Mike's Feed

Author Topics

coffeescript (1)
rails3 (10)
capybara (6)
cucumber (12)
javascript (2)
jquery (1)
webdriver (5)
carrierwave (1)
fog (1)
s3 (2)
testing (1)
rspec2 (3)
omniauth (1)
ruby (5)
devise (1)
aruba (2)
generators (2)
solr (1)
sunspot (1)
concerns (1)
mixins (1)
resque (1)
thin (1)
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >