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.

2
  • what about John Fitzgerald Kennedy? Commented May 28, 2012 at 18:43
  • Good thought. I might work something in though this is something quick and dirty - nothing robust.. Commented May 28, 2012 at 23:17

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Right. Note the difference between reverse and reverse!.
Ah, yes, forgot to mention that. Thanks :)
2

A compact version:

nameList.map!{ |x| x.split.reverse.join(' ') }
#=> ["Blow Joe", "Sue Mary", "Mallory Alice"]

Comments

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.

2 Comments

Succinct. And it looks like there's no difference between map and collect. Thanks!
@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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.