282

Is there any ready function which converts camel case Strings into underscore separated string?

I want something like this:

"CamelCaseString".to_underscore      

to return "camel_case_string".

...

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Daniel Cukier
  • 11,502
  • 15
  • 68
  • 123

12 Answers12

420

Rails' ActiveSupport adds underscore to the String using the following:

class String
  def underscore
    self.gsub(/::/, '/').
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
    gsub(/([a-z\d])([A-Z])/,'\1_\2').
    tr("-", "_").
    downcase
  end
end

Then you can do fun stuff:

"CamelCase".underscore
=> "camel_case"
Paweł Gościcki
  • 9,066
  • 5
  • 70
  • 81
jrhicks
  • 14,759
  • 9
  • 42
  • 57
  • 6
    If you change `tr("-","_")` to `tr("- ","_")` (space added to first param), this will also turn spaces into underscores. Also, I don't think you even need to include `self.`, or at least it works for me under Ruby 1.9.3. – Gus Shortz Jul 17 '13 at 15:39
  • 9
    `require 'active_support/core_ext/string'` – konsolebox Jan 26 '16 at 06:39
  • The underscore function Active uses: https://github.com/rails/rails/blob/fc5dd0b85189811062c85520fd70de8389b55aeb/activesupport/lib/active_support/inflector/methods.rb#L92 – Katrina Aug 21 '18 at 18:00
  • underscore is method of Rails not ruby, see https://apidock.com/rails/String/underscore . – S.Yadav Sep 23 '19 at 12:41
  • 1
    @S.Yadav true the method is a Rails so you can't call underscore by default, but by providing the code used and citing rails as the source this answer is a good one for ruby, It says you can include rails, or just include this function that the rails team wrote and have tested. – Michael Gorman Nov 06 '19 at 16:44
  • @GusShortz Why hasn't the Rails core added your fix to this method, because `.underscore` is basically useless without it. – Kelsey Hannan Apr 15 '21 at 11:42
147

You can use

"CamelCasedName".tableize.singularize

Or just

"CamelCasedName".underscore

Both options ways will yield "camel_cased_name". You can check more details it here.

oxfist
  • 749
  • 6
  • 22
abo-elleef
  • 2,100
  • 1
  • 14
  • 16
  • 16
    This is a part of ActiveSupport::Inflector's implementation, without it, you can't use those String extensions (try in pure irb console: "ThisCamelCaseString".underscore, then require 'active_support/inflector' and try again) – Evgeniya Manolova Jan 30 '14 at 20:19
  • 6
    The label of the OP does say "ruby-on-rails" – Julien Lamarche Jul 25 '19 at 15:59
57

One-liner Ruby implementation:

class String
   # ruby mutation methods have the expectation to return self if a mutation occurred, nil otherwise. (see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-gsub-21)
   def to_underscore!
     gsub!(/(.)([A-Z])/,'\1_\2')
     downcase!
   end

   def to_underscore
     dup.tap { |s| s.to_underscore! }
   end
end

So "SomeCamelCase".to_underscore # =>"some_camel_case"

Samy Dindane
  • 17,900
  • 3
  • 40
  • 50
kirushik
  • 1,296
  • 1
  • 10
  • 20
  • 3
    how are the other solutions not pure ruby? – jrhicks Oct 02 '09 at 15:01
  • Oh, sh... Thanks - I was more interested in writing than in reading. As a result - links on Rails made me think those other snippets to be Rails-specific. Changed answer... – kirushik Oct 02 '09 at 15:16
  • 1
    there is another error in my edit, and it doesn't appear that I can fix it. Here is the corrected code: https://gist.github.com/4027440 – Tim Harper Nov 06 '12 at 20:50
  • 7
    You may want to consider `/([^A-Z])([A-Z]+)/` instead, to handle `"ALLCAPS"` -> `"allcaps"` instead of `"a_ll_ca_ps"` – Nevir Dec 18 '12 at 04:34
  • 5
    That's actually a 10-liner! – kristianp Feb 25 '16 at 23:42
35

There is a Rails inbuilt method called 'underscore' that you can use for this purpose

"CamelCaseString".underscore #=> "camel_case_string" 

The 'underscore' method can typically be considered as inverse of 'camelize'

Aaditi Jain
  • 6,969
  • 2
  • 24
  • 26
26

In case someone looking for case when he need to apply underscore to string with spaces and want to convert them to underscores as well you can use something like this

'your String will be converted To underscore'.parameterize.underscore
#your_string_will_be_converted_to_underscore

Or just use .parameterize('_') but keep in mind that this one is deprecated

'your String will be converted To underscore'.parameterize('_')
#your_string_will_be_converted_to_underscore
Arman Petrosyan
  • 867
  • 13
  • 26
21

Here's how Rails does it:

   def underscore(camel_cased_word)
     camel_cased_word.to_s.gsub(/::/, '/').
       gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
       gsub(/([a-z\d])([A-Z])/,'\1_\2').
       tr("-", "_").
       downcase
   end
Pesto
  • 23,810
  • 2
  • 71
  • 76
  • 2
    Better to have the operand as a method argument rather than to invade the core String class. – Pistos Oct 02 '09 at 14:47
  • 1
    Don't agree - better to have it operating on the class like it should be, or otherwise you will have to stick it in a module and remember to include it wherever you will need it. – Ghoti Mar 15 '11 at 15:29
  • 1
    Also this method is part of Rails 3's strings anyway :) – Ghoti Mar 15 '11 at 15:45
  • 2
    If I may jump in to this debate -- Better to have it invade the string class _when_ you include it =). – Evan Moran Apr 13 '12 at 21:59
16

Short oneliner for CamelCases when you have spaces also included (doesn't work correctly if you have a word inbetween with small starting-letter):

a = "Test String"
a.gsub(' ', '').underscore
  
  => "test_string"

EDIT: As pointed out by @dft then this method is not part of Ruby but Rails.

Andres
  • 2,099
  • 3
  • 22
  • 39
11

Check out snakecase from Ruby Facets

The following cases are handled, as seen below:

"SnakeCase".snakecase         #=> "snake_case"
"Snake-Case".snakecase        #=> "snake_case"
"Snake Case".snakecase        #=> "snake_case"
"Snake  -  Case".snakecase    #=> "snake_case"

From: https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/snakecase.rb

class String

  # Underscore a string such that camelcase, dashes and spaces are
  # replaced by underscores. This is the reverse of {#camelcase},
  # albeit not an exact inverse.
  #
  #   "SnakeCase".snakecase         #=> "snake_case"
  #   "Snake-Case".snakecase        #=> "snake_case"
  #   "Snake Case".snakecase        #=> "snake_case"
  #   "Snake  -  Case".snakecase    #=> "snake_case"
  #
  # Note, this method no longer converts `::` to `/`, in that case
  # use the {#pathize} method instead.

  def snakecase
    #gsub(/::/, '/').
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
    gsub(/([a-z\d])([A-Z])/,'\1_\2').
    tr('-', '_').
    gsub(/\s/, '_').
    gsub(/__+/, '_').
    downcase
  end

  #
  alias_method :underscore, :snakecase

  # TODO: Add *separators to #snakecase, like camelcase.

end
Abram
  • 39,950
  • 26
  • 134
  • 184
8

Receiver converted to snake case: http://rubydoc.info/gems/extlib/0.9.15/String#snake_case-instance_method

This is the Support library for DataMapper and Merb. (http://rubygems.org/gems/extlib)

def snake_case
  return downcase if match(/\A[A-Z]+\z/)
  gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
  gsub(/([a-z])([A-Z])/, '\1_\2').
  downcase
end

"FooBar".snake_case           #=> "foo_bar"
"HeadlineCNNNews".snake_case  #=> "headline_cnn_news"
"CNN".snake_case              #=> "cnn"
Mr. Black
  • 11,692
  • 13
  • 60
  • 85
3

I had trouble running "CamelCaseString".underscore in a rake task. This helped me:

ActiveSupport::Inflector.underscore "CamelCaseString"
=> "camel_case_string"

Of course, you need to require ActiveSupport

Rimian
  • 36,864
  • 16
  • 117
  • 117
2

I would like this:

class String

  # \n returns the capture group of "n" index
  def snakize
    self.gsub(/::/, '/')
    .gsub(/([a-z\d])([A-Z])/, "\1_\2")
    .downcase
  end

  # or

  def snakize
    self.gsub(/::/, '/')
    .gsub(/([a-z\d])([A-Z])/) do
      "#{$1}_#{$2}"
    end
    .downcase
  end

end

Monkey patch of String class. There are class that begin with two or more letters in uppercase.

rplaurindo
  • 1,277
  • 14
  • 23
  • You need to change `"\1_\2"` to `'\1_\2'` otherwise you'll end up with `"came\u0001_\u0002ase"` instead of `"camel_case"` as of Ruby 2.5 ... maybe others as well. – 6ft Dan Apr 03 '18 at 04:53
2

The ruby core itself has no support to convert a string from (upper) camel case to (also known as pascal case) to underscore (also known as snake 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'

# convert to snake case string
LuckyCase.snake_case('CamelCaseString')      # => 'camel_case_string'
# or the opposite way
LuckyCase.pascal_case('camel_case_string')   # => 'CamelCaseString'

You can even monkey patch the String class if you want to:

require 'lucky_case/string'

'CamelCaseString'.snake_case  # => 'camel_case_string'
'CamelCaseString'.snake_case! # => 'camel_case_string' and overwriting original

Have a look at the offical repository for more examples and documentation:

https://github.com/magynhard/lucky_case

magynhard
  • 158
  • 7