116

I'm trying to get Rails to capitalize the first character of a string, and leave all the others the way they are. I'm running into a problem where "i'm from New York" gets turned into "I'm from new york."

What method would I use to select the first character?

Thanks

EDIT: I tried to implement what macek suggested, but I'm getting a "undefined method `capitalize'" error. The code works fine without the capitalize line. Thanks for the help!

def fixlistname!
  self.title = self.title.lstrip + (title.ends_with?("...") ? "" : "...")
  self.title[0] = self.title[0].capitalize
  errors.add_to_base("Title must start with \"You know you...\"") unless self.title.starts_with? 'You know you'
end

EDIT 2: Got it working. Thanks for the help!

EDIT 3: Wait, no I didn't... Here's what I have in my list model.

def fixlistname!
  self.title = self.title.lstrip + (title.ends_with?("...") ? "" : "...")
  self.title.slice(0,1).capitalize + self.title.slice(1..-1)
  errors.add_to_base("Title must start with \"You know you...\"") unless self.title.starts_with?  'You know you'
end

EDIT 4: Tried macek's edit, and still getting an undefined method `capitalize'" error. What could I be doing wrong?

def fixlistname!
  self.title = title.lstrip
  self.title += '...' unless title.ends_with?('...')
  self.title[0] = title[0].capitalize
  errors.add_to_base('Title must start with "You know you..."') unless title.starts_with?("You know you")
end

EDIT 5: This is weird. I'm able to get rid of the undefined method error by using the line below. The problem is that it seems to replace the first letter with a number. For example, instead of capitalizing the y in You, it turns the y into a 121

self.title[0] = title[0].to_s.capitalize
  • Based on `EDIT 3`, you need to start line 2 with `self.title = `. Also, on all 3 lines, you only need `self.title` on the left of an `=` (equal sign). In other places you can just use `title`. See the edit on my answer for an example. – maček Apr 15 '10 at 16:49
  • 2
    The problem in Edit4 is that you've got just a character - no longer a string - therefore it won't recognise the string-function "capitalize". – Taryn East Apr 21 '10 at 14:05
  • Problem with Edit 5 is that title[0] is a fixnum, so calling title[0].to_s will just give you the character number (i.e 121 as a string: "121"). You can do self.title[0] = title.first.capitalize if you want! – Nick B Mar 29 '12 at 22:29
  • 1
    I added a #upcase_first_case and made a pull request: https://github.com/rails/rails/pull/15319. Feel free to add your feedback there. – Aldo 'xoen' Giambelluca May 25 '14 at 19:28
  • Props to [`upcase_first`](https://stackoverflow.com/questions/2646709/capitalize-only-first-character-of-string-and-leave-others-alone-rails#answer-37336619) < answer there for anyone using Rails 5+. Credit @user1519240. Would be a good accepted answer if @DanielOConnor ever revisits this :) – SRack Oct 16 '20 at 15:01

18 Answers18

132

This should do it:

title = "test test"     
title[0] = title[0].capitalize
puts title # "Test test"
Philip
  • 6,827
  • 13
  • 75
  • 104
Pascal Van Hecke
  • 4,516
  • 3
  • 19
  • 18
  • 1
    @JonGarvin title[0].capitalize doesn't work (at least with Ruby 1.8.7), as title[0] returns a Fixnum, and capitalize expects a string.. so I think title.first.capitalize is the way to go. – Nick B Mar 29 '12 at 22:23
  • 2
    shame you can't `title.first.capitalize!` – Adam Waite Feb 05 '15 at 12:39
  • 3
    `capitalize` converts all letters after the first letter to lowercase, so it dosn't work for the string presented in the question ("i'm from New York"). – Mark Schneider Sep 19 '19 at 05:40
  • Sorry I know this is old but thought I'd respond. We are only calling capitalize on the first character here, not the whole string, so it does work. – Abe Petrillo Jan 28 '20 at 19:11
  • 1
    More shorter, `title[0] = title[0].upcase` – vrintle Nov 29 '20 at 17:16
96

Titleize will capitalise every word. This line feels hefty, but will guarantee that the only letter changed is the first one.

new_string = string.slice(0,1).capitalize + string.slice(1..-1)

Update:

irb(main):001:0> string = "i'm from New York..."
=> "i'm from New York..."
irb(main):002:0> new_string = string.slice(0,1).capitalize + string.slice(1..-1)
=> "I'm from New York..."
Taryn East
  • 27,486
  • 9
  • 86
  • 108
  • Wait, still not working. False alarm :/ I think I'm just implementing it wrong. –  Apr 15 '10 at 16:43
  • 1
    Daniel - it's generally good form to "accept" an answer that solved your problem. You can do this by clicking the "tick" to the left of the answer. :) – Taryn East Nov 16 '11 at 18:11
  • 1
    I misunderstood the question, otherwise I wanted to give you a 1 up. I was looking for .titleize instead of .capitalize. Thank you anyway Taryn! – Eric Wanchic Jun 20 '14 at 15:15
  • OOC - Why does that stop you from giving an upvote? Did I correctly answer the original poster's question? If you think so - then upvote. It doesn't have to be dependent on anything else... :) – Taryn East Jun 22 '14 at 23:14
  • Suboptimal. This fails if string is empty. – Huliax Jan 14 '15 at 15:30
  • so, stick "if string.present?" on the end... not hard – Taryn East Jan 15 '15 at 04:31
  • 2
    So, here it goes: `s[0].capitalize + s[1..-1] if s.present?` – Alex Escalante Mar 08 '16 at 03:21
66

As of Rails 5.0.0.beta4 you can use the new String#upcase_firstmethod or ActiveSupport::Inflector#upcase_first to do it. Check this blog post for more info.

So

"i'm from New York...".upcase_first

Will output:

"I'm from New York..."
dani24
  • 2,108
  • 2
  • 26
  • 28
user1519240
  • 2,186
  • 1
  • 22
  • 20
63

You can use humanize. If you don't need underscores or other capitals in your text lines.

Input:

"i'm from New_York...".humanize

Output:

"I'm from new york..."
Bartuzz
  • 829
  • 6
  • 7
58
str = "this is a Test"
str.sub(/^./, &:upcase)
# => "This is a Test"
Lasse Bunk
  • 1,848
  • 20
  • 21
  • 2
    Concise and elegant. I believe this should be the top answer. – Chris Aug 13 '16 at 20:05
  • I agree. I think this should be the accepted answer. – Garry Pettet Oct 29 '17 at 21:48
  • One quirk is that since `^` means beginning of line, `"\nfoo"` becomes `"\nFoo"`. That's probably fine for most use cases. Since it's a `sub` and not a `gsub`, it will still only upcase one letter, even with multiline strings. – Henrik N Oct 05 '18 at 10:38
  • 1
    Can get the limitation above by using `str.sub(/\S/, &:upcase)`, which will find the first non-whitespace character and upcase it. – Anthony Panozzo Jan 10 '19 at 21:25
14

An object oriented solution:

class String
  def capitalize_first_char
    self.sub(/^(.)/) { $1.capitalize }
  end
end

Then you can just do this:

"i'm from New York".capitalize_first_char
lmanners
  • 2,331
  • 17
  • 9
8
str.sub(/./, &:capitalize)
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
6
my_string = "hello, World"
my_string.sub(/\S/, &:upcase) # => "Hello, World"
Pavel Pravosud
  • 619
  • 6
  • 6
6

Edit 2

I can't seem to replicate your trouble. Go ahead and run this native Ruby script. It generates the exact output your looking for, and Rails supports all of these methods. What sort of inputs are you having trouble with?

#!/usr/bin/ruby
def fixlistname(title)
  title = title.lstrip
  title += '...' unless title =~ /\.{3}$/
  title[0] = title[0].capitalize
  raise 'Title must start with "You know you..."' unless title =~ /^You know you/
  title
end

DATA.each do |title|
  puts fixlistname(title)
end

__END__
you know you something WITH dots ...
you know you something WITHOUT the dots
  you know you something with LEADING whitespace...
  you know you something with whitespace BUT NO DOTS
this generates error because it doesn't start with you know you

output

You know you something WITH dots ...
You know you something WITHOUT the dots...
You know you something with LEADING whitespace...
You know you something with whitespace BUT NO DOTS...
RuntimeError: Title must start with "You know you..."

Edit

Based on your edit, you can try something like this.

def fixlistname!
  self.title = title.lstrip
  self.title += '...' unless title.ends_with?('...')
  self.title[0] = title[0].capitalize
  errors.add_to_base('Title must start with "You know you..."') unless title.starts_with?("You know you")
end

Original

This will do the trick

s = "i'm from New York"
s[0] = s[0].capitalize
#=> I'm from New York

When trying to use String#capitalize on the whole string, you were seeing I'm from new york because the method:

Returns a copy of str with the first character converted to uppercase and the remainder to lowercase.

"hello".capitalize    #=> "Hello"
"HELLO".capitalize    #=> "Hello"
"123ABC".capitalize   #=> "123abc"
maček
  • 76,434
  • 37
  • 167
  • 198
  • +1 cause I didn't think that would work till I tried it on my pc :) – Jeriko Apr 15 '10 at 15:57
  • Hi, thanks for the help. What am I doing wrong though? I've edited the original question to include my code. –  Apr 15 '10 at 16:22
  • I used Taryn's suggestion and got it working. Thanks for the help though! –  Apr 15 '10 at 16:38
  • Hi, stackoverflow says you made an edit, but I don't see anything different? –  Apr 15 '10 at 18:26
3

No-one's mentioned gsub, which lets you do this concisely.

string.gsub(/^([a-z])/) { $1.capitalize }

Example:

 > 'caps lock must go'.gsub(/^(.)/) { $1.capitalize }
=> "Caps lock must go"
mahemoff
  • 44,526
  • 36
  • 160
  • 222
3

Most of these answers edit the string in place, when you are just formatting for view output you may not want to be changing the underlying string so you can use tap after a dup to get an edited copy

'test'.dup.tap { |string| string[0] = string[0].upcase }
Paul.s
  • 38,494
  • 5
  • 70
  • 88
3

If and only if OP would want to do monkey patching on String object, then this can be used

class String
  # Only capitalize first letter of a string
  def capitalize_first
    self.sub(/\S/, &:upcase)
  end
end

Now use it:

"i live in New York".capitalize_first #=> I live in New York
JVK
  • 3,782
  • 8
  • 43
  • 67
2

An even shorter version could be:

s = "i'm from New York..."
s[0] = s.capitalize[0]
Saim
  • 2,471
  • 5
  • 30
  • 43
1

Note that if you need to deal with multi-byte characters, i.e. if you have to internationalize your site, the s[0] = ... solution won't be adequate. This Stack Overflow question suggests using the unicode-util gem

Ruby 1.9: how can I properly upcase & downcase multibyte strings?

EDIT

Actually an easier way to at least avoid strange string encodings is to just use String#mb_chars:

s = s.mb_chars
s[0] = s.first.upcase
s.to_s
Cœur
  • 37,241
  • 25
  • 195
  • 267
frontendbeauty
  • 2,089
  • 2
  • 17
  • 18
1

Rails starting from version 5.2.3 has upcase_first method.

For example, "my Test string".upcase_first will return My Test string.

Dragonn steve
  • 11
  • 1
  • 1
0

Perhaps the easiest way.

s = "test string"
s[0] = s[0].upcase
# => "Test string"
Lukas Baliak
  • 2,849
  • 2
  • 23
  • 26
0
"i'm from New York".camelize
=> "I'm from New York"
justi
  • 3,887
  • 2
  • 18
  • 24
  • While this may work, from a semantic point-of-view, I'm not sure that `camelize` tells me that it will capitalize the first character. In C# world, for example, we call capitalizing the first word "Pascal case," and lower case first word "camel case." – General Grievance Feb 22 '22 at 14:41
  • 1
    This is not the same: `i'm from New York C_1".camelize` => "I'm from New York C1", the "_" has disapeared – Albert Català Aug 29 '22 at 13:52
-3
string = "i'm from New York"
string.split(/\s+/).each{ |word,i| word.capitalize! unless i > 0 }.join(' ')
# => I'm from New York
Jeriko
  • 6,547
  • 4
  • 28
  • 40
  • Seems so. I'm new to ruby, and the other answers didn't actually do what the OP asked, so I made it work :D Correct me if I'm wrong, but AFAIK modifying a string by changing string[i] doesn't work in a lot of languages? – Jeriko Apr 15 '10 at 16:03
  • 3
    this is a Ruby-specific question. It doesn't matter if `string[i]` doesn't work in other languages. Please help keep StackOverflow clutter-free of these kind of hacked-up answers. We can't say RTFM, but even a *quick* glance at the `String` docs would've helped avoid an answer like this... – maček Apr 15 '10 at 16:23