alightholder
alightholder

Reputation: 115

Bringing values into a loop in Ruby

This is almost certainly a duplicate, but I can't find the original - I don't know the search terms to use. Which is why I'm on Stackoverflow instead of Google :)

Anyhow, here's my code:

def titleize(say)
  index = 0
  words = say.split
  words.each do |word|
    unless word == "and" || "or" || "over" || "the" || "for"
        word.capitalize!
    end
    if index == 0
        word.capitalize!
    end
    index += 1
  end
  say = words.join(" ")
end

Because index is declared before the loop, my if index == 0 is not working. How do I let Ruby know about and use my object index? Also: what is this called?

Upvotes: 1

Views: 97

Answers (6)

Cary Swoveland
Cary Swoveland

Reputation: 110755

Your code returns the modification of say, but does change the contents of the variable. It appears that you want to modify the argument, but I'm not sure about that. I will first suggest a way to return the modified value of say (but not alter the value of say, and then will show how you could change the code to modify the argument.

Notice that I do not employ an index, and use a case statement to determine whether words after the first should be capitalized.

Code

def titleize(say)
  words = say.split
  return "" if words.empty?
  words.first.capitalize!
  return words.first if words.size == 1
  words[1..-1].each do |word|
    case word
    when "and", "or", "over", "the", "for"
    else
      word.capitalize!
    end
  end
  words.join(' ')
end

Examples

say = "and now is the time for all Rubyists to hunker down and code"
titleize(say)
  #=> "And Now Is the Time for All Rubyists To Hunker Down and Code"
say
  #=> "and now is the time for all Rubyists to hunker down and code"    

say = "  "
titleize(say)
  #=> ""

say = " and "
titleize(say)
  #=> "And"

Modifying the Argument

If you wish to modify the argument say, use String#replace:

def titleize_and_modify_arg(say)
  words = say.split
  str =
  case words.size
  when 0
    ""
  when 1
    words.first.capitalize
  else
    words.first.capitalize!
    words[1..-1].each do |word|
      case word
      when "and", "or", "over", "the", "for"
      else
        word.capitalize!
      end
    end
    words.join(' ')
  end  
  say.replace(str)
end

say = "and now is the time for all Rubyists to hunker down and code"
titleize_and_modify_arg(say)
  #=> "And Now Is the Time for All Rubyists To Hunker Down and Code"
say
  #=> "And Now Is the Time for All Rubyists To Hunker Down and Code"

say = " and "
titleize_and_modify_arg(say)
  #=> nil
say
  #=> " and "

Notice that in the second example, titleize_and_modify_arg modifies say correctly, but returns nil. Of course, the method could be easily changed to return the value of say, as well as changing it, if that were desired.

Note also that, in the case statement, when words.siz => 1, it's capitalize, not capitalize!, as the latter would return nil if the word is already capitalized. capitalize! is need for the else case, however.

Upvotes: 0

engineersmnky
engineersmnky

Reputation: 29613

I would do this it is more flexible and more ruby-esque

def titleize(sentence,exclusions=[])
  sentence.split.map.with_index do |word,index|
    (index == 0 || !exclusions.include?(word)) ? word.capitalize : word
  end.join(' ')
end

For this case i used 'capitalize' without the bang in case any of the words are already capitalized.

"Hello".capitalize! #=> nil
"Hello".capitalize #=> "Hello"

It will also let you re-use the same list of exclusion or change them as you see fit

Call as

exclude = ["and", "or", "over", "the", "for"]
titleize("hello there you are over there", exclude)
#=> "Hello There You Are over There"

Upvotes: 0

tadman
tadman

Reputation: 211750

That's not how booleans work. The way this is evaluated is:

x == 'a' || 'b'

Becomes:

(x == 'a') || 'b'

Which is equivalent to:

'b'

What you're intending, translated to more idiomatic Ruby, is:

def titleize(say)
  say.split.each_with_index do |word, index|
    if (index == 0)
      word.capitalize!
    else
      case (word)
      when "a", "and", "or", "over", "the", "for"
        # Leave lower-case
      else
        word.capitalize!
      end
    end
  end.join(' ')
end

titleize('the time this is a test for the things!')
# => "The Time This Is a Test for the Things!"

Upvotes: 1

ave4224
ave4224

Reputation: 19

I recommend using each_index instead of each. See here.

Try this:

def titleize (say)
  words = say.split
  words.each_index do |index|
    word = words[i]
    unless word == "and" || "or" || "over" || "the" || "for"
        word.capitalize!
    end
    if index == 0
        word.capitalize!
    end
  end
  say = words.join(" ")
end

Upvotes: -3

Andy Gaskell
Andy Gaskell

Reputation: 31761

I think you want to use with_index. Your word comparison was busted too.

def titleize(say)
  words = say.split
  l = ["and", "or", "over", "the", "for"]

  words.each.with_index do |word, index|
    word.capitalize! if index == 0 || !(l.include? word)
  end

  say = words.join(" ")
end

puts(titleize("hello there for you"))
puts(titleize("hi"))
puts(titleize("for"))

Upvotes: 2

BroiSatse
BroiSatse

Reputation: 44725

Using index == 0 is perfectly fine as index is accessible within your loop. Your real problem is probably in this line:

word == "and" || "or" || "over" || "the" || "for"

This is always true-like! What you mean is:

["and", "or", "over", "the", "for"].include? word

Apart form that there is a method called each_with_index, which you can use like this:

words.each_with_index do |word, index|

Upvotes: 8

Related Questions