Convert "ThisIsSomeString" to "this_is_some_string"?

Hi all

What's the fastest way to convert "ThisIsSomeString" to
"this_is_some_string"?

Thanks for help,
Joshua

···

--
Posted via http://www.ruby-forum.com/.

harp:~ > cat a.rb
   def snake_case string
     return string unless string =~ %r/[A-Z]/
     string.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?/).reverse.map{|word| word.reverse.downcase}.join '_'
   end

   def camel_case string
     return string if string =~ %r/[A-Z]/ and string !~ %r/_/
     words = string.strip.split %r/\s*_+\s*/
     words.map!{|w| w.downcase.sub(%r/^./){|c| c.upcase}}
     words.join
   end

   s = "ThisIsSomeString"
   p(snake_case(s))

   s = "this_is_some_string"
   p(camel_case(s))

   harp:~ > ruby a.rb
   "this_is_some_string"
   "ThisIsSomeString"

regards.

-a

···

On Sat, 19 Aug 2006, Joshua Muheim wrote:

What's the fastest way to convert "ThisIsSomeString" to
"this_is_some_string"?

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to "this_is_some_string"?

Thanks for help,
Joshua

p "ThisIsSomeString".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
p "this_is_some_string".split('_').map{|w|w.capitalize}.join

lopex

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to "this_is_some_string"?

I believe this is the fastest way, though it may not catch all cases (numbers and such)

   def lowerize(str) # ??
     str.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
   end

Cheers,
Daniel

If you have Rails installed:

require 'action_pack'
"ThisIsSomeString".underscore
=> "this_is_some_string"

-- Scott

Joshua Muheim wrote:

···

Hi all

What's the fastest way to convert "ThisIsSomeString" to
"this_is_some_string"?

Thanks for help,
Joshua

--
Posted via http://www.ruby-forum.com/.

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to
"this_is_some_string"?

Thanks for help,
Joshua

--
Posted via http://www.ruby-forum.com/.

p ["FooBar","fooBar","FOOBar","FooBAR"].map{|s|
  s.gsub(/.(?=[A-Z])/,'\&_').downcase }

["foo_bar", "foo_bar", "f_o_o_bar", "foo_b_a_r"]

I like to do string.split(/(?=[A-Z])/).map { |s| s.downcase }.join('_')

···

On Aug 19, 2006, at 10:49 AM, Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to
"this_is_some_string"?

Thanks for help,
Joshua

--
Posted via http://www.ruby-forum.com/.

Thanks a lot for your help. :slight_smile:

···

--
Posted via http://www.ruby-forum.com/.

Marcin Mielżyński wrote:

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to
"this_is_some_string"?

Thanks for help,
Joshua

p "ThisIsSomeString".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
p "this_is_some_string".split('_').map{|w|w.capitalize}.join

lopex

syntax error near unexpected token `('

:frowning:

···

--
Posted via http://www.ruby-forum.com/.

irb(main):001:0> "FooBAR".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
=> "foo"

it's trickier than it looks.

-a

···

On Sun, 20 Aug 2006, [UTF-8] Marcin MielżyÅ~Dski wrote:

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to "this_is_some_string"?

Thanks for help,
Joshua

p "ThisIsSomeString".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
p "this_is_some_string".split('_').map{|w|w.capitalize}.join

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

depending on what you want, the snake_case code i posted works differently:

   irb(main):025:0> lowerize 'BARFoo'
   => "barfoo"

   irb(main):026:0> snake_case 'BARFoo'
   => "bar_foo"

neither is right - just fyi.

-a

···

On Sun, 20 Aug 2006, Daniel Schierbeck wrote:

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to "this_is_some_string"?

I believe this is the fastest way, though it may not catch all cases (numbers and such)

def lowerize(str) # ??
   str.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
end

Cheers,
Daniel

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

require 'active_support' is enough.

···

On 19-aug-2006, at 19:10, Scott wrote:

If you have Rails installed:

require 'action_pack'
"ThisIsSomeString".underscore
=> "this_is_some_string"

--
Julian 'Julik' Tarkhanov
please send all personal mail to
me at julik.nl

Joshua Muheim wrote:

Marcin Mielżyński wrote:

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to "this_is_some_string"?

Thanks for help,
Joshua

p "ThisIsSomeString".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
p "this_is_some_string".split('_').map{|w|w.capitalize}.join

lopex

syntax error near unexpected token `('

:frowning:

works for me, maybe some typo ?

lopex

How about this one?

class MyString < String
   def snake_case(group = true)
     return self if self == ''
     if group
       if self =~ /\b(?:[A-Z]+)(?:[A-Z][a-z])/
         gsub(/\b([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/\s/,'_').downcase
       else
         gsub(/([a-zA-Z])([A-Z])/,'\1_\2').gsub(/\s/,'_').downcase
       end
     else
       gsub(/([a-zA-Z])(?=[A-Z])/,'\1_\2').gsub(/\s/,'_').downcase
     end
   end
end

a = MyString.new("fooBar")
puts a.snake_case
b = MyString.new("FOOBar")
puts b.snake_case
c = MyString.new("FOOOOBar")
puts c.snake_case

When ran:

foo_bar
foooo_bar

Kirk Haines

Correct. Your solution has a flaw, too.

   "fooBar".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_') => "bar"

Lets see if we can solve this without peeking at the Rails source :slight_smile:

Cheers,
Daniel

···

ara.t.howard@noaa.gov wrote:

On Sun, 20 Aug 2006, Daniel Schierbeck wrote:

Joshua Muheim wrote:

Hi all

What's the fastest way to convert "ThisIsSomeString" to "this_is_some_string"?

I believe this is the fastest way, though it may not catch all cases (numbers and such)

def lowerize(str) # ??
   str.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
end

Cheers,
Daniel

depending on what you want, the snake_case code i posted works differently:

  irb(main):025:0> lowerize 'BARFoo'
  => "barfoo"

  irb(main):026:0> snake_case 'BARFoo'
  => "bar_foo"

neither is right - just fyi.

> Joshua Muheim wrote:
>> Hi all
>>
>> What's the fastest way to convert "ThisIsSomeString" to
>> "this_is_some_string"?
>>
>> Thanks for help,
>> Joshua
>>
>
> p "ThisIsSomeString".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
> p "this_is_some_string".split('_').map{|w|w.capitalize}.join
>

irb(main):001:0> "FooBAR".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_')
=> "foo"

it's trickier than it looks.

Oh, look, a mini-quiz!

"FooBAR".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map { |w| w.downcase}.join('_')
=> "foo_bar"

"FooBARbaz".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map { |w|
w.downcase}.join('_')
=> "foo_bar_baz"

"FooBarBaz".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map { |w|
w.downcase}.join('_')
=> "foo_bar_baz"

Now what to do about non-alphas? The above breaks:

"Foo123Bar".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map { |w|
w.downcase}.join('_')
=> "foo_bar"

"Foo123Bar".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map {

w> w.downcase}.join('_')

=> "foo_123_bar"

"FooBar123".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map {

w> w.downcase}.join('_')

=> "foo_bar_123"

"123FooBar".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map {

w> w.downcase}.join('_')

=> "123_foo_bar"

Or we might want to coalesce non-alpha sub-strings with a preceding
alpha string:

"Foo123Bar".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map {

w> w.downcase}.join('_')

=> "foo123_bar"

"FooBar123".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map {

w> w.downcase}.join('_')

=> "foo_bar123"

But:

"123FooBar".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map {

w> w.downcase}.join('_')

=> "foo_bar"

This covers that case as well:

"123FooBar".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map
{ |w| w.downcase}.join('_')
=> "123_foo_bar"

"Foo123Bar".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map
{ |w| w.downcase}.join('_')
=> "foo123_bar"

"FooBar123".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map
{ |w| w.downcase}.join('_')
=> "foo_bar123"

And here's a way to do it with gsub:

"FooBar123".gsub(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/)
{|w| w.downcase + '_'}[0..-2]
=> "foo_bar123"

"Foo123Bar".gsub(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/)
{|w| w.downcase + '_'}[0..-2]
=> "foo123_bar"

"123FooBar".gsub(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/)
{|w| w.downcase + '_'}[0..-2]
=> "123_foo_bar"

The scan and join approach seems to be marginally faster:
$ ruby benchsnake.rb
Rehearsal --------------------------------------------
scanjoin 0.790000 0.030000 0.820000 ( 0.883282)
gsub 1.030000 0.020000 1.050000 ( 1.106962)
----------------------------------- total: 1.870000sec

               user system total real
scanjoin 0.810000 0.020000 0.830000 ( 1.011059)
gsub 0.970000 0.040000 1.010000 ( 1.050524)

== benchsnake.rb ===
require 'benchmark'
include Benchmark

iterations = 10000
bmbm do | x |
        x.report("scanjoin") do
              iterations.times do

"FooBar123".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/).map
{ |w| w.downcase}.join('_')
              end
        end

        x.report("gsub") do
                iterations.times do

"FooBar123".gsub(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+[^a-zA-Z]*)?/)
{|w| w.downcase + '_'}[0..-2]
                end
        end
end

···

On 8/19/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Sun, 20 Aug 2006, [UTF-8] Marcin MielżyÅ~Dski wrote:

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

depending on what you want, the snake_case code i posted works differently:

  irb(main):025:0> lowerize 'BARFoo'
  => "barfoo"

  irb(main):026:0> snake_case 'BARFoo'
  => "bar_foo"

neither is right - just fyi.

Correct. Your solution has a flaw, too.

"fooBar".scan(/[A-Z][a-z]+/).map{|w|w.downcase}.join('_') => "bar"

good catch!

Lets see if we can solve this without peeking at the Rails source :slight_smile:

here's a start:

harp:~ > cat a.rb
def snake_case string
   return string unless string =~ %r/[A-Z]/
   string.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?|[^A-Z]+/).reverse.map{|word| word.reverse.downcase}.join '_'
end

def camel_case string
   return string if string =~ %r/[A-Z]/ and string !~ %r/_/
   words = string.strip.split %r/\s*_+\s*/
   words.map!{|w| w.downcase.sub(%r/^./){|c| c.upcase}}
   words.join
end

if $0 == __FILE__
   require 'test/unit'
   require 'enumerator'

   class T < Test::Unit::TestCase
     tests = {
       "snake_case" => %w[
         ThisIsSomeString this_is_some_string
         fooBar foo_bar
         FooBar foo_bar
         Foo foo
       ],

       "camel_case" => %w[
         this_is_some_string ThisIsSomeString
         foo_bar FooBar
         foo Foo
         foo_bar_foobar FooBarFoobar
       ]
     }

     tests.each do |meth, list|
       testno = -1
       list.each_slice(2) do |arg, expected|
         define_method "test_#{ meth }_#{ testno += 1 }" do
           actual = send meth, arg
           assert_equal expected, actual
         end
       end
     end
   end
end

harp:~ > ruby a.rb
Loaded suite a
Started
........
Finished in 0.001527 seconds.

8 tests, 8 assertions, 0 failures, 0 errors

cheers.

-a

···

On Sun, 20 Aug 2006, Daniel Schierbeck wrote:
--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

Can we agree that "ABCde" => "ab_cde"?

I still get an error when the original string has underscores in it:

   "foo_Bar" => "foo__bar"
   "foo_BAR" => "foo__bar"

Too tired to figure out a solution right now, I'll get back to you in the morning (that'll probably be your night.)

Cheers,
Daniel

···

ara.t.howard@noaa.gov wrote:

here's a start:

harp:~ > cat a.rb
def snake_case string
  return string unless string =~ %r/[A-Z]/
  string.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?|[^A-Z]+/).reverse.map{|word| word.reverse.downcase}.join '_'
end

here's a start:

harp:~ > cat a.rb
def snake_case string
  return string unless string =~ %r/[A-Z]/
  string.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?|[^A-Z]+/).reverse.map{|word| word.reverse.downcase}.join '_'
end

Can we agree that "ABCde" => "ab_cde"?

sure. it's arbitrary i realize - but i make sense to me! :wink:

I still get an error when the original string has underscores in it:

"foo_Bar" => "foo__bar"
"foo_BAR" => "foo__bar"

Too tired to figure out a solution right now, I'll get back to you in the morning (that'll probably be your night.)

ok. i actually use this method in some production code so a better impl would
be nice!

here's the latest, which fixes bug above:

harp:~ > cat a.rb
def snake_case string
   return string unless string =~ %r/[A-Z]/
   reversed_words = string.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?|[^A-Z]+/)
   reversed_words.reverse.map{|word| word.reverse.downcase}.join('_').gsub(%r/_+/,'_')
end

def camel_case string
   return string if string =~ %r/[A-Z]/ and string !~ %r/_/
   words = string.strip.split %r/\s*_+\s*/
   words.map!{|w| w.downcase.sub(%r/^./){|c| c.upcase}}
   words.join
end

if $0 == __FILE__
   require 'test/unit'
   require 'enumerator'

   class T < Test::Unit::TestCase
     tests = {
       "snake_case" => %w[
         ThisIsSomeString this_is_some_string
         fooBar foo_bar
         FooBar foo_bar
         Foo foo
         Foo_Bar foo_bar
         foo_Bar foo_bar
         foo_BAR foo_bar
       ],

       "camel_case" => %w[
         this_is_some_string ThisIsSomeString
         foo_bar FooBar
         foo Foo
         foo_bar_foobar FooBarFoobar
       ]
     }

     tests.each do |meth, list|
       testno = -1
       list.each_slice(2) do |arg, expected|
         define_method "test_#{ meth }_#{ testno += 1 }" do
           actual = send meth, arg
           assert_equal expected, actual
         end
       end
     end
   end
end

harp:~ > ruby a.rb
Loaded suite a
Started
...........
Finished in 0.003574 seconds.

11 tests, 11 assertions, 0 failures, 0 errors

-a

···

On Sun, 20 Aug 2006, Daniel Schierbeck wrote:

ara.t.howard@noaa.gov wrote:

--
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dali lama

I'm not sure you want to label these as bugs:

   "FOO_Bar" => "fo_o_bar"
   "FOO_bar" => "fo_o_bar"

But this probably is:

   "FOO_BAR" => "fo_o_bar"

Cheers,
Daniel

···

ara.t.howard@noaa.gov wrote:

def snake_case string
  return string unless string =~ %r/[A-Z]/
  reversed_words = string.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?|[^A-Z]+/)
  reversed_words.reverse.map{|word| word.reverse.downcase}.join('_').gsub(%r/_+/,'_')