59

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!

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
wsb3383
  • 3,841
  • 12
  • 44
  • 59
  • Related question: http://stackoverflow.com/questions/5615597/why-does-rails-titlecase-add-a-space-to-a-name/ – Andrew Grimm Apr 14 '11 at 02:34
  • Does this answer your question? [Converting camel case to underscore case in ruby](https://stackoverflow.com/questions/1509915/converting-camel-case-to-underscore-case-in-ruby) – Eyeslandic Aug 13 '20 at 22:11

11 Answers11

103

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.

Matthew
  • 1,300
  • 12
  • 30
James A. Rosen
  • 64,193
  • 61
  • 179
  • 261
  • 7
    Perfect answer, everyone else is re-inventing the wheel and you are using the wheels provided which is a main point of using Ruby on Rails – Michael Durrant Jul 31 '11 at 20:35
  • 14
    Please note that he answered concerning Rails. If you are using Rails, this answer is the best. But if you are using only Ruby, you might not want to require the whole ActiveSupport library just to titleize a string. There are efficiency considerations here. But while I'm at it, why don't they have "titleise" for Britons? – mjnissim Jun 07 '12 at 06:41
  • 8
    `titleize` is fine for this use case (capitalizing an all upper case string), but there is kind of a gotcha for mixed case: `'jOhn'.titleize # => "J Ohn"`. This just bit me. – fabi Nov 01 '13 at 17:03
  • 1
    @mjnissim if you just need the [Rails string inflections](http://api.rubyonrails.org/v2.3.8/classes/ActiveSupport/CoreExtensions/String/Inflections.html), you can `require 'active_support/core_ext/string/inflections'`, no? – Dennis May 13 '14 at 04:44
  • 1
    @Dennis Yes I think you're right. Good comment, thank you. So when titleising without Rails, first `require 'active_support/core_ext/string/inflections'` and then use `.titleize`. This will just require what is needed - more efficient. – mjnissim May 13 '14 at 05:47
  • question was about Ruby, not rails – Connor Leech May 15 '14 at 11:55
61
"HELLO WORLD HOW ARE YOU".gsub(/\w+/) do |word|
  word.capitalize
end
#=> "Hello World How Are You"
sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 6
    Excellent and terse. Explanation: gsub replaces all occurrences. The regex means 'any substring consisting of a word character (`\w'), followed by 0 or more word characters (`+`)." Note that this can be a one-liner if you use {|word| word.capitalize} instead of `do` and `end` - it's just a matter of preference. – Nathan Long Oct 23 '10 at 13:22
  • 1
    You could also easily add a conditional statement to only capitalize certain words - `if wordArray.include?(word) word.capitalize else word` (add line breaks to run) – Nathan Long Oct 23 '10 at 13:31
  • 2
    The one-liner worked nicely for me in an erb template loop over a list of filenames I wanted to capitalize while retaining the hyphens: `"name-another-third".gsub(/\w+/){|word| word.capitalize}` => "Name-Another-Third – Dave Everitt Aug 19 '13 at 21:24
  • 7
    For those looking for a one-liner - you can also do `"HELLO WORLD".gsub(/\w+/, &:capitalize)` – Nick Mar 17 '15 at 02:42
37

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.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Note that Facet's `titlecase` does not convert any character from upper to lower case. The actual example, `"MY STRING HERE".titlecase` returns `"MY STRING HERE"`. You need `"MY STRING HERE".downcase.titlecase` to get the requested output. – Dan Apr 16 '20 at 19:20
9

From ActiveSupport

"MY STRING HERE".gsub(/\b('?[a-z])/) { $1.capitalize }

If you are using Rails/ActiveSupport, the method is already available for free.

Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
5
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 (" ").

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
bantic
  • 4,886
  • 4
  • 29
  • 34
  • 7
    Note that this will turn multiple consecutive spaces into one and break if the string contains newlines or tabs. – sepp2k Nov 24 '09 at 17:39
3

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)

Community
  • 1
  • 1
Adobe
  • 12,967
  • 10
  • 85
  • 126
2
"MY STRING HERE".titlecase

Does the job (it's a method in the Rails gem, however) http://apidock.com/rails/String/titlecase

marcindobry
  • 176
  • 1
  • 5
1

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

0

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
K.S.A.
  • 1
  • 1
0

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

magynhard
  • 158
  • 7
-1

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
Connor Leech
  • 18,052
  • 30
  • 105
  • 150