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/