2

I'm trying to reverse words in an array of string variables, but split doesn't seem to be working.

Testing in IRB I get "NoMethodError: private method `split' called for Array", which I'm assuming has something to do with my program quietly doing nothing.

For example, I have:

nameList = ["Joe Blow", "Mary Sue", "Alice Mallory"].

I expect to return:

["Blow Joe", "Sue Mary", "Mallory Alice"].

So I iterate through the array, splitting, reversing and joining. This is where nothing happens:

nameList.each { |x| 
  x.to_s.split(' ').reverse!.join(' ')
  puts x   #testing here
}

This outputs:

Joe Blow
Mary Sue
Alice Mallory

I must be missing something extremely simple, as this can't be too difficult.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
thesubtlety
  • 45
  • 1
  • 1
  • 5

3 Answers3

7

You're splitting, reversing and discarding the result. Check this out.

nameList = ["Joe Blow", "Mary Sue", "Alice Mallory"]

nameList.each { |x| 
  puts x.to_s.split(' ').reverse.join(' ')
  puts x
  puts '' # for easier reading   
}
# >> Blow Joe
# >> Joe Blow
# >> 
# >> Sue Mary
# >> Mary Sue
# >> 
# >> Mallory Alice
# >> Alice Mallory
# >> 

If you want to apply some transformation to every element of array, get new value and construct a new array of these values, it is idiomatic to use Array#map function.

nameList = ["Joe Blow", "Mary Sue", "Alice Mallory"]

newList = nameList.map { |x| 
  x.to_s.split(' ').reverse.join(' ')
}

Also, here you shouldn't use bang version of reverse (reverse!). It has destructive semantics. reverse creates a new reversed array, while reverse! updates source array in place. In this case source array is a temp variable, so it makes no difference result-wise. But I consider it confusing and distracting.

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
2

A compact version:

nameList.map!{ |x| x.split.reverse.join(' ') }
#=> ["Blow Joe", "Sue Mary", "Mallory Alice"]
megas
  • 21,401
  • 12
  • 79
  • 130
0

The Problem

Everything in Ruby has a return value. In this case, you aren't using the return value of your method chain; you're just printing the original value.

nameList.each { |x| 
  # Returns a result, but you aren't doing anything with it.
  x.to_s.split(' ').reverse!.join(' ')

  # Here you print your original value.
  puts x   #testing here
}

The Solution

The simple way to do this is to use #collect, which returns an array.

p nameList.collect { |name| name.split.reverse.join(' ') }
["Blow Joe", "Sue Mary", "Mallory Alice"]
=> ["Blow Joe", "Sue Mary", "Mallory Alice"]

This prints a modified array, and also returns an array as a result for further processing. Alternatively, if you really want to change your array, you can assign the result like so:

nameList = nameList.collect { |name| name.split.reverse.join(' ') }
 => ["Blow Joe", "Sue Mary", "Mallory Alice"] 
nameList
 => ["Blow Joe", "Sue Mary", "Mallory Alice"]

There are certainly other ways to do this, but it's important to keep it readable. In this case, it makes sense to maintain the semantics of assigning an array to your variable so that the intent is clear.

Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • Succinct. And it looks like there's [no difference](http://stackoverflow.com/questions/5254732/ruby-difference-between-map-and-collect) between map and collect. Thanks! – thesubtlety May 28 '12 at 23:44
  • @thesubtlety: exactly, they are aliases. It's matter of preference, I guess. I, for one, can't stand names `collect` and `inject`. I just can't memorize which one of them does what :) `map` and `reduce` seem so much more natural. – Sergio Tulentsev May 29 '12 at 08:17