How to make a string Title Case in Ruby [duplicate] - ruby

def titleize(string)
nocaps = ["the","and"]
puts string.split(" ").map { |word| nocaps.include?(word) ? word : word.capitalize }.join(" ")
end
titleize("the bridge over the river kwai")
------------------------------------------------------------------------------------------
######Render => "the Bridge Over the River Kwai"
######Expected rendering => "The Bridge Over the River Kwai"
Hello i present you my piece of code, the rendering gives
"the Bridge Over the River Kwai"
While I would like
"The Bridge Over the River Kwai"
So I would like to find a way to find an exception on the same word.

your code, but always capitalise the first letter, using each_with_index to get the position in the array:
def titleize(string)
nocaps = ["the","and"]
string.split(" ").each_with_index.map { |word, i| i.positive? && nocaps.include?(word) ? word : word.capitalize }.join(" ")
end
titleize("the bridge over the river kwai")

Capitalize String Before Per-Word Capitalization
There are a lot of ways to do this, but one way is to capitalize the whole String object before deciding whether or not to capitalize individual words. For example:
def titleize str
stopwords = %w[the and]
title_words = str.capitalize.split.map! do |word|
stopwords.include?(word) ? word : word.capitalize
end.join ?\s
end
p titleize("the bridge over the river kwai")
#=> "The Bridge Over the River Kwai"
p titleize("the bridge over the river kwai and the amazon")
#=> "The Bridge Over the River Kwai and the Amazon"
Under the hood this will actually downcase everything but the first letter, and then programmatically capitalize each word that isn't in stopwords. So, it relies on some implicit behavior, but works well for the examples posted.

One way is as follows.
def titleize(str, little_words)
str.downcase.split.map.with_index do |word,i|
little_words.include?(word) && i > 0 ? word : word.capitalize
end.join(" ")
end
str = "the briDge over The river kwai"
titleize(str, ["the", "and"])
#=> "The Bridge Over the River Kwai"
Here's a second way that operates on the string directly (rather than converting it to an array or words, perform substitutions for those words and then joining the resulting array):
def titleize(str, little_words)
str.downcase.gsub(/(?<= )\p{L}+/) do |s|
little_words.include?(s) ? s : s.capitalize
end
end
titleize(str, ["the", "and"])
#=> "The Bridge Over the River Kwai"
Notice that this method preserves extra spaces between words in str.
The regular expression reads, "match one or more Unicode letters (\p{L}+) (greedily) preceded by a space".
A variant of this is to capitalize all words not in little_words and then capitalize the first character of the resulting string:
def titleize(str, little_words)
str.downcase.gsub(/\p{L}+/) do |s|
little_words.include?(s) ? s : s.capitalize
end.tap { |s| s[0] = s[0].upcase }
end
See Object#tap.
If little_words contains many words the methods could be sped up by first converting that array to a set little_words_set (require 'set'; little_words_set = little_words.to_set) and then substituting little_words_set wherever little_words now appears.
This can be improved upon by taking a page from #Todd's answer: replace downcase with capitalize, obviating the need for the tap clause.
Note that little words is a term used in the publishing industry.

While the other answers are to the point of your original post I thought I would offer another alternative using a regular expression.
def titleize(str)
nocaps = ["the","and"]
str.gsub(/\A.|\b(!?#{Regexp.union(nocaps)})\b\w+/,&:capitalize)
end
titleize("the bridge over the river and through the woods kwai")
#=> "The Bridge Over the River and Through the Woods Kwai"
This regex will select the first letter \A. and any other words that are not contained in the nocaps Array then it will substitute each selected word with capitalized version.
The resulting regex in this case is /\A.|\b(?!(?-mix:the|and))\b\w+/ Example

Related

RUBY | Find a way to find an exception on the same word to capitalize

def titleize(string)
nocaps = ["the","and"]
puts string.split(" ").map { |word| nocaps.include?(word) ? word : word.capitalize }.join(" ")
end
titleize("the bridge over the river kwai")
------------------------------------------------------------------------------------------
######Render => "the Bridge Over the River Kwai"
######Expected rendering => "The Bridge Over the River Kwai"
Hello i present you my piece of code, the rendering gives
"the Bridge Over the River Kwai"
While I would like
"The Bridge Over the River Kwai"
So I would like to find a way to find an exception on the same word.
your code, but always capitalise the first letter, using each_with_index to get the position in the array:
def titleize(string)
nocaps = ["the","and"]
string.split(" ").each_with_index.map { |word, i| i.positive? && nocaps.include?(word) ? word : word.capitalize }.join(" ")
end
titleize("the bridge over the river kwai")
Capitalize String Before Per-Word Capitalization
There are a lot of ways to do this, but one way is to capitalize the whole String object before deciding whether or not to capitalize individual words. For example:
def titleize str
stopwords = %w[the and]
title_words = str.capitalize.split.map! do |word|
stopwords.include?(word) ? word : word.capitalize
end.join ?\s
end
p titleize("the bridge over the river kwai")
#=> "The Bridge Over the River Kwai"
p titleize("the bridge over the river kwai and the amazon")
#=> "The Bridge Over the River Kwai and the Amazon"
Under the hood this will actually downcase everything but the first letter, and then programmatically capitalize each word that isn't in stopwords. So, it relies on some implicit behavior, but works well for the examples posted.
One way is as follows.
def titleize(str, little_words)
str.downcase.split.map.with_index do |word,i|
little_words.include?(word) && i > 0 ? word : word.capitalize
end.join(" ")
end
str = "the briDge over The river kwai"
titleize(str, ["the", "and"])
#=> "The Bridge Over the River Kwai"
Here's a second way that operates on the string directly (rather than converting it to an array or words, perform substitutions for those words and then joining the resulting array):
def titleize(str, little_words)
str.downcase.gsub(/(?<= )\p{L}+/) do |s|
little_words.include?(s) ? s : s.capitalize
end
end
titleize(str, ["the", "and"])
#=> "The Bridge Over the River Kwai"
Notice that this method preserves extra spaces between words in str.
The regular expression reads, "match one or more Unicode letters (\p{L}+) (greedily) preceded by a space".
A variant of this is to capitalize all words not in little_words and then capitalize the first character of the resulting string:
def titleize(str, little_words)
str.downcase.gsub(/\p{L}+/) do |s|
little_words.include?(s) ? s : s.capitalize
end.tap { |s| s[0] = s[0].upcase }
end
See Object#tap.
If little_words contains many words the methods could be sped up by first converting that array to a set little_words_set (require 'set'; little_words_set = little_words.to_set) and then substituting little_words_set wherever little_words now appears.
This can be improved upon by taking a page from #Todd's answer: replace downcase with capitalize, obviating the need for the tap clause.
Note that little words is a term used in the publishing industry.
While the other answers are to the point of your original post I thought I would offer another alternative using a regular expression.
def titleize(str)
nocaps = ["the","and"]
str.gsub(/\A.|\b(!?#{Regexp.union(nocaps)})\b\w+/,&:capitalize)
end
titleize("the bridge over the river and through the woods kwai")
#=> "The Bridge Over the River and Through the Woods Kwai"
This regex will select the first letter \A. and any other words that are not contained in the nocaps Array then it will substitute each selected word with capitalized version.
The resulting regex in this case is /\A.|\b(?!(?-mix:the|and))\b\w+/ Example

RE-capitalizing the first word

So it's apparent this question has been asked before, but what I'm actually asking is specific to the code I am writing. Basically I'm capitalizing the words (titleizing). My method is not optimized, and it does go in circles so just bear with me. I can't seem to recapitalize the first word of the title once I made it lowercased again. I have written comments in the code, so you can just breeze through it without analyzing the entire thing. I'm not asking you to write a new code because I can just google that. I'm more interested in why my solutions aren't working..
input: "the hamster and the mouse"
output: "the Hamster and the Mouse"
WHAT I WANT: "The Hamster and the Mouse"
class String
def titleize
#regex reads: either beginning of string or whitespace followed by alpha
self.gsub(/(\A|\s)[a-z]/) do |letter|
letter.upcase!
end
end
end
class Book
attr_accessor :title
def title=(title)
#title = title.titleize #makes every word capitalized
small_words = %w[In The And A An Of]
words = #title.split(" ")
#makes all the "small_words" uncapitalized again
words.each do |word|
if small_words.include?(word)
word.downcase!
end
end
words[0][0].upcase! #doesnt work
#title = words.join(" ")
#NEED TO MAKE FIRST WORD CAPITALIZED EVEN IF ITS A "small_word"
#title[0].upcase! #also doesnt work
end
end
Replace words[0][0].upcase! with words[0] = words[0].titleize. This will titleize the first word in the title, which is what you want.
You also don't need #title[0].upcase!.
Change the last line from:
#title[0].upcase!
To:
#title.capitalize!
EDIT:
I rewrote the class. Fewer lines and you don't need RegEx or String#titleize method.
class Book
attr_reader :title
def title=(title)
small_words = ["in", "the", "and", "a", "an", "of"]
#title = title.split.each do |word|
small_words.include?(word.downcase) ? word.downcase! : word.capitalize!
end
#title[0].capitalize!
#title = #title.join(" ")
end
end
new_book = Book.new
new_book.title="the hamster and the mouse"
new_book.title # => "The Hamster and the Mouse"

The pig latin translation

I am trying to solve the "pig latin problem" in the Test-First Ruby lessons.
In this program I am basically trying to translate a string with the following rules:
If a word begins with a vowel sound, add an "ay" sound to the end of the word.
If a word begins with a consonant sound, move it to the end of the word, and then add an "ay" sound to the end of the word.
For this I wrote the following code which worked fine:
def translate(word)
words=word.split(" ")
words.each do |x|
if ["a","e","i","o","u"].include?x[0,1]
x << ("ay")
else
x << ("#{x[0,1]}ay")
x[0,1]=""
end
end
words.join(" ")
end
However, the problem also states that when translating words with 2, or 3 consonants in the beginning, it should move them all at the end of the word, and then add "ay".
For that I ended an until loop into the else statement:
def translate(word)
words=word.split(" ")
words.each do |x|
if ["a","e","i","o","u"].include?x[0,1]
x << ("ay")
else
until ["a","e","i","o","u"].include?x[0,1]
x << ("#{x[0,1]}")
x[0,1]=""
end
x << ("#{x[0,1]}ay")
end
end
words.join(" ")
end
This is giving me this result:
translate("the bridge over the river kwai")
=> "etheay idgebriay overay etheay iverriay aikwaay"
So, it is running the until loop one extra time and adding the first vowel in the word to the end as well. However, it is not removing this vowel from the first position.
What am I doing wrong?
It's this line: x << ("#{x[0,1]}ay").
You've already shaved off the consonants from the beginning of the word so that it starts with a vowel, and then you're adding that vowel ("#{x[0,1]}") to the end along with the ay.
So, replace x << ("#{x[0,1]}ay") with just x << "ay" and it should work.
(NOTE: technically this is not an answer)
Your original code is not very idiomatic. You're running while loops and mutating strings in-place. You don't see that in good ruby code. May I offer you an improved version?
def vowel?(str)
["a","e","i","o","u"].include?(str)
end
def translate_word(word)
first_vowel_idx = word.chars.find_index{|c| vowel?(c)}
leading_consonants = word[0..first_vowel_idx-1]
rest_of_the_word = word[first_vowel_idx..-1]
rest_of_the_word + leading_consonants + 'ay'
end
def translate(sentence)
words = sentence.split(" ")
words.map{|w| translate_word(w) }.join(" ")
end
translate("the bridge over the river kwai") # => "ethay idgebray overoveray ethay iverray aikway"

How do I capitalize all words in a string apart from small words in the middle and in the beginning?

I have a long string, "the silver rider on his back and the palm tree". I would like to write a Ruby method that capitalizes all words except "on", "the", and "and" in the middle of the sentence, but have the "the" capitalized at the beginning?
Here is what I have so far:
def title(word)
small_words = %w[on the and]
word.split(' ').map do |w|
unless small_words.include?(w)
w.capitalize
else
w
end
end.join(' ')
end
This code actually does most of what I need but don't know how to include or exclude for that matter the "the" at the beginning of the sentence.
This will capitalize all the words, except for the stop words (your small words) that aren't the first in the sentence.
def title(sentence)
stop_words = %w{a an and the or for of nor} #there is no such thing as a definite list of stop words, so you may edit it according to your needs.
sentence.split.each_with_index.map{|word, index| stop_words.include?(word) && index > 0 ? word : word.capitalize }.join(" ")
end
It’s easiest to just forget about the special case of the first letter initially and then handle it after doing everything else:
def title(sentence)
small_words = %w[on the and]
capitalized_words = sentence.split(' ').map do |word|
small_words.include?(word) ? word : word.capitalize
end
capitalized_words.first.capitalize!
capitalized_words.join(' ')
end
This also capitalizes any “small word” at the beginning, not just “the”—but I think that’s probably what you want anyway.
A simple mod to your existing code would make it work:
def title( word )
small_words = %w[on the and]
word.split(' ').map.with_index do |w, i|
unless (small_words.include? w) and (i > 0)
w.capitalize
else
w
end
end.join(' ')
end
SmallWords = %w[on the and]
def title word
word.gsub(/[\w']+/){
SmallWords.include?($&) && $~.begin(0).zero?.! ? $& : $&.capitalize
}
end

Ruby: Pig Latin: Method to iterate through multiple words (not working)

Sorry, yet another question regarding the TestFirst.org Ruby exercise to write a 'Pig Latin' method, coming from a newbie. The other answers helped somewhat but I wasn't able to adapt them successfully. The main issue is that I'm trying to write a method to scan through a string of words (not just a single word), modify some of the words (if applicable), then return the full string back.
Below is my code attempting to perform the first part of the exercise, which is to append "ay" to any word beginning with a vowel. However, it is not working for me -- seems the .include? never returns true from comparing with a single letter(?)
Any help is much appreciated!
# PIG LATIN
# If any word within the input string begins with a vowel, add an "ay" to the end of the word
def translate(string)
vowels_array = %w{a e i o u y}
consonants_array = ('a'..'z').to_a - vowels_array
string_array = string.split
string_array.each do |word|
if vowels_array.include?(word[0])
word + 'ay'
end
end
return string_array.join(" ")
end
translate("apple orange mango") # => "appleay orangeay mango" but does not
string_array.each just iterates through string_array, doesn't change it; in order to update the contents of an array you should use map!:
# ...
string_array.map! do |word|
if vowels_array.include?(word[0])
word + 'ay'
else
word
end
end
# ...
translate("apple orange mango") #=> "appleay orangeay mango"
The purpose of else word end is to return the word also when the if condition is not satisfied.
Out of the array manipulating point of view, in most cases the best way to manipulate strings are regexps:
def translate(string)
string.gsub(/(^|\s)[aeiouy]\S*/i, '\0ay')
end
translate("apple orange mango") #=> "appleay orangeay mango"
Hash key lookup may be a bit faster
v= Hash['a', 1, 'o', '1', 'i', 1, 'u', 1, 'e', 1]
ws= %w(apple orange mango)
ws.map! do |w|
v[w[0]].nil? ? w : "#{w}ay"
end
p ws
Sounds like a job for a regular expression:
str = 'apple orange mango'
str.gsub(/\b[aeiou]\w*\b/, '\0ay')
#=> "appleay orangeay mango"
gsub will look for all occurences of a pattern (the first argument) and replace it with a string (the second argument). Inside that string, you can refer back to the matched pattern with \0 and append ay to it, which leaves us with \0ay.
Now the pattern (the actual regular expression) means “Capture whole words (\b matches word boundaries), that start with one of [aeiou] and end with zero ore more word characters (\w*)”.
So your complete method can be boiled down to:
def translate(string)
string.gsub /\b[aeiou]\w*\b/, '\0ay'
end
Et voilá!
Try:
def translate(string)
new_string = ''
string.split.each do |word|
if 'aoiue'.include?(word[0])
new_string += word + 'ay '
else
new_string += word + ' '
end
end
return new_string.strip
end
> translate("apple orange mango")
=> "appleay orangeay mango"

Resources