Convert "ThisIsSomeString" to "this_is_some_string"?

J

Julian 'Julik' Tarkhanov

If you have Rails installed:

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


require 'active_support' is enough.
 
R

Rick DeNatale

Joshua said:
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('_'= )
=3D> "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=
('_')
=3D> "foo_bar"

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

"FooBarBaz".scan(/[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map { |w|
w.downcase}.join('_')
=3D> "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('_')
=3D> "foo_bar"

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

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

"123FooBar".scan(/[^a-zA-Z]+|[a-z]+|[A-Z](?:[A-Z]+|[a-z]+)?/).map {
|w| w.downcase}.join('_')
=3D> "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('_')
=3D> "foo123_bar"

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

But:

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

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

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

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

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

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

iterations =3D 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



--=20
Rick DeNatale

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,209
Messages
2,571,089
Members
47,689
Latest member
kilaocrhtbfnr

Latest Threads

Top