Mengonversi string dari snake_case ke CamelCase di Ruby

171

Saya mencoba mengubah nama dari case ular ke case unta. Apakah ada metode bawaan?

Misalnya: "app_user"untuk"AppUser"

(Saya punya string yang "app_user"ingin saya konversi menjadi model AppUser).

Lohith MV
sumber

Jawaban:

252

Jika Anda menggunakan Rails, String # camelize adalah yang Anda cari.

  "active_record".camelize                # => "ActiveRecord"
  "active_record".camelize(:lower)        # => "activeRecord"

Jika Anda ingin mendapatkan kelas yang sebenarnya, Anda harus menggunakan String # constantize di atas itu.

"app_user".camelize.constantize
Sergio Tulentsev
sumber
44
Anda harus menambahkan bahwa ini adalah tambahan Rails ke String, itu tidak berfungsi dengan Ruby murni.
iGEL
2
Ini ditandai ruby-on-rails, jadi, saya kira, itu tidak masalah. Tapi terima kasih sudah menyebutkan.
Sergio Tulentsev
6
Anda tidak perlu menyamarkan sebelum melakukan stabilisasi. Gunakan #classifysebagai gantinya. "some_namespace/module/class_name".classify => "SomeNamespace::Module::ClassName"
Chris Heald
5
@chris #classify: Tidak sama. #classify mengembalikan sebuah string, sedangkan #constantize mencari konstanta dalam konteks (dan memang perlu camelize). 'active_record'.constantize memberikan kesalahan,' active_record'.camelize.constantize mengembalikan ActiveRecord yang konstan, 'active_record'.classify mengembalikan string' ActiveRecord '. Dan jika Anda melakukan 'no_class'.camelize.constantize Anda akan mendapatkan kesalahan (tidak ada NoClass konstan), tetapi' no_class'.classify dengan senang hati mengembalikan string 'NoClass'.
Kanat Bolazar
Untuk menggunakan metode Rails ini dari Ruby murni, require "active_support/core_ext/string"sudah cukup, menyediakan Rails sudah diinstal.
Masa Sakano
121

Bagaimana dengan yang ini?

"hello_world".split('_').collect(&:capitalize).join #=> "HelloWorld"

Ditemukan dalam komentar di sini: Klasifikasi string Ruby

Lihat komentar Wayne Conrad

pengguna3869936
sumber
11
Kamu luar biasa, terima kasih. Saya tidak mau harus menyertakan rel perpustakaan hanya untuk tugas sekecil ini. Ini indah. :)
Gerry
11
Ini adalah satu-satunya jawaban nyata untuk pertanyaan itu. Tidak menggunakan perpustakaan Rails.
Luis Ortega Araneda
40

Jika Anda menggunakan Rails, Gunakan classify. Ini menangani kasus tepi dengan baik.

"app_user".classify # => AppUser
"user_links".classify   # => UserLink

catatan:

Jawaban ini khusus untuk uraian yang diberikan dalam pertanyaan (tidak spesifik untuk judul pertanyaan). Jika seseorang mencoba mengkonversi string ke camel-case, mereka harus menggunakan jawaban Sergio . Penanya menyatakan bahwa ia ingin mengkonversi app_userke AppUser(tidak App_user), maka jawaban ini ..

Harish Shetty
sumber
4
Untuk lingkungan Rails, ini sempurna.
ghayes
Catatan yang classifymengembalikan string, Anda harus memanggilnya constantizesetelah itu untuk mengubahnya menjadi kelas aktual.
Stefan
1
Peringatan penting dengan classifyadalah bahwa string jamak akan menjadi tunggal ... 'age_in_years'.classifymenjadiAgeInYear
br3nt
@ br3nt tidak jamak sejak activerecord4.2.11
Ulysse BN
23

Sumber: http://rubydoc.info/gems/extlib/0.9.15/String#camel_case-instance_method

Untuk tujuan belajar:

class String
  def camel_case
    return self if self !~ /_/ && self =~ /[A-Z]+.*/
    split('_').map{|e| e.capitalize}.join
  end
end

"foo_bar".camel_case          #=> "FooBar"

Dan untuk varian lowerCase:

class String
  def camel_case_lower
    self.split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
  end
end

"foo_bar".camel_case_lower          #=> "fooBar"
Tuan hitam
sumber
6
@ pguardiario jika roda ini disebut ActiveSupport , harap temukan kembali.
Shime
Saya pikir varian lowerCase salah. Blok injeksi tidak boleh secara langsung memanipulasi buffer tetapi mengembalikan nilai baru untuk buffer:self.split('_').inject([]){ |buffer,e| buffer + [buffer.empty? ? e : e.capitalize] }.join
Sven Koschnicke
19

Benchmark untuk solusi Ruby murni

Saya mengambil setiap kemungkinan yang ada dalam pikiran saya untuk melakukannya dengan kode ruby ​​murni, ini dia:

  • bermodalkan dan gsub

    'app_user'.capitalize.gsub(/_(\w)/){$1.upcase}
  • pisah dan petakan menggunakan &steno (terima kasih atas jawaban pengguna3869936)

    'app_user'.split('_').map(&:capitalize).join
  • pisah dan petakan (terima kasih untuk jawaban Tuan Black)

    'app_user'.split('_').map{|e| e.capitalize}.join

Dan di sini adalah Tolok Ukur untuk semua ini, kita dapat melihat bahwa gsub sangat buruk untuk ini. Saya menggunakan 126 080 kata.

                              user     system      total        real
capitalize and gsub  :      0.360000   0.000000   0.360000 (  0.357472)
split and map, with &:      0.190000   0.000000   0.190000 (  0.189493)
split and map        :      0.170000   0.000000   0.170000 (  0.171859)
Ulysse BN
sumber
11

Saya tiba di sini untuk mencari kebalikan dari pertanyaan Anda, dari kasus unta ke kasus ular. Gunakan garis bawah untuk itu (bukan decamelize):

AppUser.name.underscore # => "app_user"

atau, jika Anda sudah memiliki tali unta:

"AppUser".underscore # => "app_user"

atau, jika Anda ingin mendapatkan nama tabel, yang mungkin itulah mengapa Anda menginginkan kotak ular:

AppUser.name.tableize # => "app_users"

mike
sumber
Kenapa tidak menggunakan AppUser.table_name? Anda juga akan memastikan memiliki nama tabel yang sebenarnya jika bukan app_users, tetapi sesuatu yang didefinisikan di tempat lain.
Ulysse BN
3

Saya merasa sedikit tidak enak untuk menambahkan lebih banyak jawaban di sini. Memutuskan untuk menggunakan pendekatan ruby ​​murni yang paling mudah dibaca dan minimal, mengabaikan tolok ukur yang bagus dari @ ulysse-bn. Meskipun :classmode adalah salinan dari @ user3869936, :methodmode yang tidak saya lihat di jawaban lain di sini.

  def snake_to_camel_case(str, mode: :class)
    case mode
    when :class
      str.split('_').map(&:capitalize).join
    when :method
      str.split('_').inject { |m, p| m + p.capitalize }
    else
      raise "unknown mode #{mode.inspect}"
    end
  end

Hasilnya adalah:

[28] pry(main)> snake_to_camel_case("asd_dsa_fds", mode: :class)
=> "AsdDsaFds"
[29] pry(main)> snake_to_camel_case("asd_dsa_fds", mode: :method)
=> "asdDsaFds"
akostadinov
sumber
1
Kasing unta sebenarnya lebih rendah dulu. Kalau tidak, itu disebut PascalCase (atau terkadang unta atas). Meskipun dalam pertanyaan ini ambigu!
Ulysse BN
2
@ UlysseBN, tbh saya tidak ke dalam sejarah kata-kata. Klaim Wikipedia PascalCaseadalah bagian dari CamelCase. Juga ini yang saya tahu - bahwa unta berlaku untuk keduanya. Tapi saya belum pernah menyelidiki. Terima kasih telah menyebutkan PascalCase. en.wikipedia.org/wiki/Camel_case
akostadinov
2
Ini adalah jawaban terbaik di halaman imo. Akan lebih baik jika :methodversi melakukan yang downcasepertama sehingga dapat digunakan pada keduanya lower_snake_casedan UPPER_SNAKE_CASE.
skagedal
0

Sebagian besar metode lain yang tercantum di sini adalah Rails khusus. Jika Anda ingin melakukan ini dengan Ruby murni, berikut ini adalah cara paling ringkas yang saya buat (terima kasih kepada @ ulysse-bn untuk perbaikan yang disarankan)

x="this_should_be_camel_case"
x.gsub(/(?:_|^)(\w)/){$1.upcase}
    #=> "ThisShouldBeCamelCase"
masukomi
sumber
Definisi "unta" Anda terlalu terbatas. Nama-nama kelas di Java, dan Ruby, misalnya, adalah unta kasus MyFavoriteClass ... tetapi mereka juga tidak memiliki huruf awal yang lebih rendah. kadang-kadang kasing memiliki topi awal. terkadang tidak.
masukomi
Menggunakan 2 Regex di mana Anda hanya dapat menggunakan satu adalah berlebihan. Anda hanya dapat menggunakan grup yang tidak menangkap:x.gsub(/(?:_|^)(\w)/){$1.upcase}
Ulysse BN
@UlysseBN, dan kami kembali ke gsubsolusi Anda sepertinya lebih lambat dibandingkan dengan mapsolusinya.
akostadinov
0

Perpanjang String untuk Menambahkan Camelize

Di Ruby murni Anda bisa memperluas kelas string menggunakan kode yang sama persis dari Rails .camelize

class String
  def camelize(uppercase_first_letter = true)
    string = self
    if uppercase_first_letter
      string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
    else
      string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
    end
    string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub("/", "::")
  end
end
Cameron Lowell Palmer
sumber