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"
Related
I need to do the following with Ruby:
Turn this string of names "joseph jeremiah bloggs" into "J.J.Bloggs"
It has to work for any number of names with the last name always the full word and the other names initials.
I have the following code so far:
def initials(name)
name.split.map(&:capitalize).join('.')
end
Which returns "Joseph.Jeremiah.Bloggs"
Is there a way to get the initials for the first two words?
This is one way that follows your code:
def initials(name)
*rest, last = name.split
(rest.map{|e| e[0]} << last).map(&:capitalize).join('.')
end
Using the splat * makes rest collect all the names except the last.
You can do it like this:
"joseph jeremiah bloggs"
.gsub(/\w+\s*/){|s| ($'.empty? ? s : "#{s[0]}.").capitalize} #'
# => "J.J.Bloggs"
or
"joseph jeremiah bloggs"
.gsub(/\w+\s+/){|s| "#{s[0].upcase}."}.sub(/\w+\z/, &:capitalize)
# => "J.J.Bloggs"
Updated Solution (even though this thread is closed I still want to help others). I feel like this solution is easier for newer Ruby developers to understand.
def initialize_name(name)
name = name.split(" ")
name.map {|n| n.equal?(name.last) ? n.capitalize : n[0].capitalize }.join(". ")
end
initialize_name "joseph jeremiah blogg" # => J. J. Blogg
Old Solution
This is what I came up with. Not the prettiest but it works, I am going to try and make this look nicer for you though.
joe = "joseph jeremiah blogg"
def initialize_name(name)
return_value = ""
name = name.split(" ")
name.each do |n|
if n.equal? name.last
return_value << "#{n.capitalize}"
else
return_value << "#{n[0].capitalize}. "
end
end
return_value
end
puts initialize_name joe # => "J. J. Blogg"
Another solution loosely building on presented code:
def initials(name)
name.scan(/([[:alpha:]])[[:alpha:]]*/).flatten.join.upcase
end
Use [[:alpha:]] (a "POSIX bracket expression") instead of the classic \w to match non-ASCII characters which do come up in names. The regex matches whole words but only captures first letters.
def initials(name)
(name.split.drop(1).map do |subname|
subname[0].upcase
end + [name.split.last]).join('.')
end
initials("Bob Smith Jones")
I can't find the solution for this problem, I did a research in order to find the problems and fix them but can't think for any answer yet.
What I want to do is to convert a string into a Title cased string.
For example:
"The Lord Of The Rings" > "The Lord of the Rings"
(As you can see, the first word is always capitalized, it doesn't matter if it's an article, but if there are articles words in the string, that should be in downcase, as the example above, and capitalize any other words that aren't).
This is the spec (RSpec) of the exercise I'm trying to solve:
describe "Title" do
describe "fix" do
it "capitalizes the first letter of each word" do
expect( Title.new("the great gatsby").fix ).to eq("The Great Gatsby")
end
it "works for words with mixed cases" do
expect( Title.new("liTTle reD Riding hOOD").fix ).to eq("Little Red Riding Hood")
end
it "downcases articles" do
expect( Title.new("The lord of the rings").fix ).to eq("The Lord of the Rings")
expect( Title.new("The sword And The stone").fix ).to eq("The Sword and the Stone")
expect( Title.new("the portrait of a lady").fix ).to eq("The Portrait of a Lady")
end
it "works for strings with all uppercase characters" do
expect( Title.new("THE SWORD AND THE STONE").fix ).to eq("The Sword and the Stone")
end
end
end
And here's my try, what I have so far:
class Title
def initialize(string)
#string = string
end
def fix
#string.split.each_with_index do |element, index|
if index == 0
p element.capitalize!
elsif index == 1
if element.include?('Is') || element.include?('is')
p element.downcase!
end
end
end
end
end
a = Title.new("this Is The End").fix
p a
Output:
"This"
"is"
=> ["This", "is", "The", "End"]
What I tried to do:
Create a class named Title and initialize it with a string.
Create a method called fix that so far, only checks for the index 0
of the #string.split with the method .each_with_index (looping
through), and prints the element.capitalize! (note the 'bang', that
should modify the original string, as you can see it in the output
above)
What my code does is to check for the index 1 (second word) and
calls the .include?('is') to see if the second word is an article,
if it is (with if statements) the element.downcase! is called,
if not, I could create more checkings for index (but what I realized
here is that some strings could be formed by 3 words, others by 5,
others by 10, and so on, so my code is not efficient for that,
that's a problem I can't solve.
Maybe creating a list of article words and check with the .include? method if there's some word of the list? (I tried this one but the .include? method only accepts a string not an array variable, I tried the join(' ') method but had no luck).
Thanks a lot! Really!
I like to break these types of problems up into smaller chunks of logic to help me understand before I write an algorithm. In this case you need to modify each word of the string based on some rules.
If it's the first word, capitalize it.
If it's not a special word, capitalize it.
If it's a special word AND it's not the first word, downcase it.
With these rules you can write your logic to follow.
special_words = ['a', 'an', 'and', 'of', 'the']
fixed_words = []
#string.downcase.split.each_with_index do |word, index|
# If this isn't the first word, and it's special, use downcase
if index > 0 and special_words.include?(word)
fixed_words << word
# It's either the first word, or not special, so capitalize
else
fixed_words << word.capitalize
end
end
fixed_words.join(" ")
You'll notice I'm using downcase on the string before calling split and each_with_index. This is so that all the words get normalized a downcase and can be easily checked against the special_words array.
I'm also storing these transformed words in an array and joining them back together in the end. The reason for that, is if I try to use downcase! or capitalize! on the split strings, I'm not modifying the original title string.
Note: This problem is part of the Bloc Full Stack course work which is why I'm using a simplified solution, rather than one liners, modules, file io, etc.
I'd create a new file in the same folder as your program (in my example that file is called "exclude.txt" and put words like "and, the" all on a new line. Then I'd do something like this:
class String
def capitalize_first_letter
self[0].upcase + self[1..-1]
end
end
class Title
def initialize(string)
#string = string
#exclude_words = File.readlines('exclude.txt').map(&:downcase).map(&:chomp)
end
def fix
#string.downcase.gsub(/\S+/) do |word|
#exclude_words.include?(word) ? word : word.capitalize
end.capitalize_first_letter
end
end
Assuming your exclude.txt file contains the words you want to remain downcase (mine contains of, on, a, is, the, and), all of your tests should get passed:
p Title.new("the great gatsby").fix #=> "The Great Gatsby"
I believe a module is more appropiate than a class here, since there's no need for "instances of titles" - you begin with strings and end with strings. If titles had more methods, maybe a class would be necessary. Here's my implementation:
The piece you were missing is Array#map. In general, learning as many methods as you can from the base ruby classes (Array, String, Hash, etc) is always a good investment.
module Title
DEFAULT_NON_CAPITALIZED_WORDS = %w{a an the and but for on at to or}
def self.titleize(str, nocaps = DEFAULT_NON_CAPITALIZED_WORDS)
str = str.downcase.split.map{|w| nocaps.include?(w) ? w : w.capitalize}.join(' ')
str[0].capitalize + str[1..-1]
end
end
Tests:
puts Title.titleize("the great gatsby")
puts Title.titleize("liTTle reD Riding hOOD")
puts Title.titleize("The sword And The stone")
puts Title.titleize("THE SWORD AND THE STONE")
EDIT: It can be done in a single long line, but it requires a regular expression to do the initial capitalization:
str.downcase.split.map{|w| nocaps.include?(w) ? w : w.capitalize}.join(' ').sub(/^./, &:upcase)
Since you want the output as a string, a module method is perhaps more appropriate. The following module allows for easy extension, and is relatively clear.
It looks like this:
module Title
SMALL_WORDS = %w(a an the at by for in of on to up and as but it or nor)
def self.titleize(str)
# Return the original string if it is empty
return str if str.match(/\A\w*\Z)
# Split the name into an array of words
words = name.split(' ')
# Capitalize every word that is not a small word
words.each do |word|
word[0] = word[0].upcase unless SMALL_WORDS.include? word.downcase
end
# Capitalize the first and last words
words.each_with_index do |word, index|
if index == 0 || index == words.count - 1
word[0] = word[0].upcase
end
end
# Return the words as a string
words.join(' ')
end
end
And it works like this:
Title.titleize('the USA swimming association')
# => 'The USA Swimming Association'
Title.titleize('the great charter of the liberties of england')
# => 'The Great Charter of the Liberties of England'
Title.titleize(' a history of of ')
# => 'A History of Of'
There are several edge cases to consider when creating a nice titleize function:
The last word should be capitalized no matter what (even if it is a word like "of")
Acronyms (like "USA") should be preserved
We need to use the each_with_index and the if statements. Notice how I make sure they ignore the first article "the" making using index > 0. Good Luck!
class Title
attr_accessor :string
Articles = %w( an the of a and )
def initialize(string)
#string = string
end
def fix
fixed_words = []
#string.downcase.split.each_with_index do |word, index|
if index > 0 && Articles.include?(word)
fixed_words << word
else
fixed_words << word.capitalize
end
end
fixed_words.join(" ")
end
end
p Title.new("the great gatsby").fix
p Title.new("liTTle reD Riding hOOD").fix
p Title.new("The lord of the rings").fix
p Title.new("The sword And The stone").fix
p Title.new("the portrait of a lady").fix
p Title.new("THE SWORD AND THE STONE").fix
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
I have the following program:
class Matcher
include Enumerable
def initialize(string, match)
#string = string
#match = match
end
def each
#string.scan(/[##match]/) do |pattern|
yield pattern
end
end
end
mch = Matcher.new("the quickbrown fox", "aeiou")
puts mch.inject {|x, n| x+n}
It is supposed to match the characters, aeiou with the string the quickbrown fox
No matter what I put as the pattern, it oddly prints out the characters: thc. What's going on?
#string.scan(/[##match]/) do |pattern| is incorrect. #{#match} is what you're looking for.
I'm trying to convert an all-uppercase string in Ruby into a lower case one, but with each word's first character being upper case. Example:
convert "MY STRING HERE" to "My String Here".
I know I can use the .downcase method, but that would make everything lower case ("my string here"). I'm scanning all lines in a file and doing this change, so is there a regular expression I can use through ruby to achieve this?
Thanks!
If you're using Rails (really all you need is ActiveSupport, which is part of Rails), you can use titleize:
"MY STRING HERE".titleize
# => "My String Here"
If you're using plain Ruby but don't mind loading a small amount of ActiveSupport you can require it first:
require 'active_support/core_ext/string/inflections'
# => true
"MY STRING HERE".titleize
# => "My String Here"
N.B. By default titleize doesn't handle acronyms well and will split camelCaseStrings into separate words. This may or may not be desirable:
"Always use SSL on your iPhone".titleize
# => "Always Use Ssl On Your I Phone"
You can (partially) address this by adding "acronyms":
require 'active_support/core_ext/string/inflections' # If not using Rails
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'SSL'
inflect.acronym 'iPhone'
end
"Always use SSL on your iPhone".titleize
# => "Always Use SSL On Your IPhone"
For those who speak the Queen's English (or who struggle to spell titleize), there's no .titleise alias but you can use .titlecase instead.
"HELLO WORLD HOW ARE YOU".gsub(/\w+/) do |word|
word.capitalize
end
#=> "Hello World How Are You"
While trying to come up with my own method (included below for reference), I realized that there's some pretty nasty corner cases. Better just use the method already provided in Facets, the mostest awesomest Ruby library evar:
require 'facets/string/titlecase'
class String
def titleize
split(/(\W)/).map(&:capitalize).join
end
end
require 'test/unit'
class TestStringTitlecaseAndTitleize < Test::Unit::TestCase
def setup
#str = "i just saw \"twilight: new moon\", and man! it's crap."
#res = "I Just Saw \"Twilight: New Moon\", And Man! It's Crap."
end
def test_that_facets_string_titlecase_works
assert_equal #res, #str.titlecase
end
def test_that_my_own_broken_string_titleize_works
assert_equal #res, #str.titleize # FAIL
end
end
If you want something that more closely complies to typical writing style guidelines (i.e. does not capitalize words like "and"), there are a couple of "titleize" gems on GitHub.
From ActiveSupport
"MY STRING HERE".gsub(/\b('?[a-z])/) { $1.capitalize }
If you are using Rails/ActiveSupport, the method is already available for free.
string = "MY STRING HERE"
string.split(" ").map {|word| word.capitalize}.join(" ")
The way this works:
The .split(" ") splits it on spaces, so now we have an array that looks like ["my", "string", "here"]. The map call iterates over each element of the array, assigning it to temporary variable word, which we then call capitalize on. Now we have an array that looks like ["My", "String", "Here"], and finally we turn that array back into a string by joining each element with a space (" ").
"MY STRING HERE".titlecase
Does the job (it's a method in the Rails gem, however)
http://apidock.com/rails/String/titlecase
Unicode-aware titlecase for Ruby 2.4.0+:
class String
def titlecase
split(/([[:alpha:]]+)/).map(&:capitalize).join
end
end
>> "я только что посмотрел \"леди исчезает\", и это чума!".titlecase
=> "Я Только Что Посмотрел \"Леди Исчезает\", И Это Чума!"
(based on https://stackoverflow.com/a/1792102/788700)
To catch any edge case such as:
str = "rUby on rAils"
Don't use:
str.titleize
Output: R Uby On R Ails
Use instead:
str.downcase.titleize
Output: Ruby On Rails
I've try to improve code... ready for critics and suggestions.
class Book
attr_accessor :title
def title=(new_title)
notcap=%w(and the a in of an)
str=''
new_title.gsub(/(\w|\s)\w+/) do |word|
word.strip!
if not notcap.include? word
word.capitalize!
end
str += ' ' + word
end
str.strip!
str = str[0].upcase + str[1..-1]
#title = str
end
end
The ruby core itself has no support to convert a string from upper (word) case to capitalized word case.
So you need either to make your own implementation or use an existing gem.
There is a small ruby gem called lucky_case which allows you to convert a string from any of the 10+ supported cases to another case easily:
require 'lucky_case'
# to get capital word case as string
LuckyCase.capital_word_case('MY STRING HERE') # => 'My String Here'
# or the opposite way
LuckyCase.upper_word_case('Capital Word Case') # => 'MY STRING HERE'
You can even monkey patch the String class if you want to:
require 'lucky_case/string'
'MY STRING HERE'.capital_word_case # => 'My String Here'
'MY STRING HERE'.capital_word_case! # => 'My String Here' and overwriting original
Have a look at the offical repository for more examples and documentation:
https://github.com/magynhard/lucky_case
Capitalizes every word in a sentence using ruby, without regex.. because unfortunately those scare me
class Book
attr_accessor :title
def title=(new_title)
result = []
words = new_title.split(' ')
words.each do |word|
capitalized = word[0].upcase + word[1..word.length].downcase
result.push(capitalized)
end
#title = result.join(' ')
end
end