Chuyển đổi chuỗi từ Snake_case sang CamelCase trong Ruby


171

Tôi đang cố gắng chuyển đổi một tên từ trường hợp rắn sang trường hợp lạc đà. Có phương pháp tích hợp nào không?

Vd: "app_user"để"AppUser"

(Tôi có một chuỗi "app_user"tôi muốn chuyển đổi nó thành mô hình AppUser).

Câu trả lời:


251

Nếu bạn đang sử dụng Rails, String # camelize là thứ bạn đang tìm kiếm.

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

Nếu bạn muốn có được một lớp thực tế, bạn nên sử dụng Chuỗi # hằng số trên đầu trang đó.

"app_user".camelize.constantize

44
Bạn nên thêm rằng đây là một bổ sung Rails cho String, nó không hoạt động với Ruby thuần túy.
iGEL

2
Nó được gắn thẻ ruby-on-rails, vì vậy, tôi đoán, nó không phải là một vấn đề. Nhưng cảm ơn đã đề cập.
Sergio Tulentsev

6
Bạn không cần phải camelize trước khi liên tục. Sử dụng #classifythay thế. "some_namespace/module/class_name".classify => "SomeNamespace::Module::ClassName"
Chris Heald

5
@chris # classify: Không giống nhau. # classify trả về một chuỗi, trong khi #constantize tìm kiếm không đổi trong ngữ cảnh (và không cần camelize). 'active_record'.constantize đưa ra lỗi,' active_record'.camelize.constantize trả về hằng số ActiveRecord, 'active_record'. classify trả về chuỗi' ActiveRecord '. Và nếu bạn đã làm 'no_group'.camelize.constantify bạn sẽ gặp lỗi (không có NoClass liên tục như vậy), nhưng' no_group'. Classify vui vẻ trả về chuỗi 'NoClass'.
Kanat Bolazar

Để sử dụng các phương thức Rails này từ Ruby thuần túy, require "active_support/core_ext/string"là đủ, cung cấp Rails đã được cài đặt.
Masa Sakano

120

Cái này thì sao?

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

Tìm thấy trong các ý kiến ​​ở đây: Phân loại chuỗi Ruby

Xem bình luận của Wayne Conrad


10
Bạn thật tuyệt vời, cảm ơn bạn. Tôi không muốn bao gồm các thư viện đường ray chỉ cho một nhiệm vụ nhỏ này. Thật là đẹp :)
Gerry

11
Đây là một trong những câu trả lời thực sự duy nhất cho câu hỏi. Không sử dụng thư viện Rails.
Luis Ortega Araneda

40

Nếu bạn sử dụng Rails, hãy sử dụng classify. Nó xử lý các trường hợp cạnh tốt.

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

Ghi chú:

Câu trả lời này là cụ thể cho mô tả được đưa ra trong câu hỏi (nó không cụ thể cho tiêu đề câu hỏi). Nếu một người đang cố gắng chuyển đổi một chuỗi thành trường hợp lạc đà, họ nên sử dụng câu trả lời của Sergio . Người hỏi nói rằng anh ta muốn chuyển đổi app_userthành AppUser(không App_user), do đó câu trả lời này ..


4
Đối với môi trường Rails, điều này là hoàn hảo.
ghayes

Lưu ý rằng classifytrả về một chuỗi, bạn phải gọi constantizesau đó để chuyển đổi nó thành một lớp thực tế.
Stefan

1
Một cảnh báo quan trọng classifylà các chuỗi số nhiều sẽ trở thành số ít ... 'age_in_years'.classifytrở thànhAgeInYear
br3nt

@ br3nt nó không số nhiều kể từ Activerecord4.2.11
Ulysse BN

23

Nguồn: http://rubydoc.info/gems/extlib/0.9.15/String#camel_case-instance_method

Đối với mục đích học tập:

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"

Và đối với biến thể lowCase:

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"

6
@pguardiario nếu bánh xe được gọi là ActiveSupport , vui lòng phát minh lại.
shime

Tôi nghĩ rằng biến thể lowCase là sai. Khối tiêm không được thao tác trực tiếp vào bộ đệm mà trả về giá trị mới cho bộ đệm:self.split('_').inject([]){ |buffer,e| buffer + [buffer.empty? ? e : e.capitalize] }.join
Sven Koschnicke

19

Điểm chuẩn cho các giải pháp Ruby thuần túy

Tôi đã lấy mọi khả năng mà tôi có trong đầu để làm điều đó với mã ruby ​​thuần túy, đây là:

  • viết hoa và gsub

    'app_user'.capitalize.gsub(/_(\w)/){$1.upcase}
  • phân chia và lập bản đồ bằng cách sử dụng tốc &ký (nhờ câu trả lời của user3869936)

    'app_user'.split('_').map(&:capitalize).join
  • phân chia và bản đồ (nhờ câu trả lời của ông Black)

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

Và đây là Điểm chuẩn cho tất cả những điều này, chúng ta có thể thấy rằng gsub khá tệ cho việc này. Tôi đã sử dụng 126 080 từ.

                              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)

11

Tôi đến đây để tìm kiếm câu hỏi ngược của bạn, từ trường hợp lạc đà đến trường hợp rắn. Sử dụng dấu gạch dưới cho điều đó (không khử màu):

AppUser.name.underscore # => "app_user"

hoặc, nếu bạn đã có một chuỗi trường hợp lạc đà:

"AppUser".underscore # => "app_user"

hoặc, nếu bạn muốn lấy tên bảng, đó có thể là lý do tại sao bạn muốn trường hợp rắn:

AppUser.name.tableize # => "app_users"


Tại sao không sử dụng AppUser.table_name? Bạn cũng sẽ đảm bảo có tên bảng thực sự nếu đó không phải là app_users, nhưng một cái gì đó được xác định ở nơi khác.
Ulysse BN

3

Tôi cảm thấy một chút khó chịu để thêm câu trả lời ở đây. Quyết định đi theo cách tiếp cận ruby ​​tinh khiết dễ đọc và tối thiểu nhất, coi nhẹ điểm chuẩn đẹp từ @ ulysse-bn. Mặc dù :classchế độ là bản sao của @ user3869936, nhưng :methodchế độ tôi không thấy trong bất kỳ câu trả lời nào khác ở đây.

  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

Kết quả là:

[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"

1
Trường hợp lạc đà trong thực tế đầu tiên thấp hơn. Mặt khác, nó được đặt tên là PascalCase (hoặc đôi khi là trường hợp lạc đà trên). Mặc dù trong câu hỏi này là mơ hồ!
Ulysse BN

2
@UlysseBN, tbh Tôi không đi vào lịch sử của các từ. Wikipedia tuyên bố PascalCaselà một tập hợp con của CamelCase. Ngoài ra đây là những gì tôi biết - trường hợp lạc đà áp dụng cho cả hai. Nhưng tôi chưa bao giờ điều tra. Cảm ơn vì đã đề cập đến PascalCase. vi.wikipedia.org/wiki/Camel_case
akostadinov

2
Đây là câu trả lời tốt nhất trên trang imo. Sẽ thật tuyệt nếu :methodphiên bản đã làm downcaselần đầu tiên để nó có thể được sử dụng trên cả hai lower_snake_caseUPPER_SNAKE_CASE.
thảm họa

0

Hầu hết các phương pháp khác được liệt kê ở đây là Rails cụ thể. Nếu bạn muốn làm điều này với Ruby thuần túy, sau đây là cách ngắn gọn nhất mà tôi nghĩ ra (cảm ơn @ ulysse-bn vì sự cải tiến được đề xuất)

x="this_should_be_camel_case"
x.gsub(/(?:_|^)(\w)/){$1.upcase}
    #=> "ThisShouldBeCamelCase"

Định nghĩa của bạn về "trường hợp lạc đà" là quá hạn chế. Ví dụ, tên lớp trong Java và Ruby trường hợp lạc đà MyFavoriteClass ... nhưng chúng cũng không có chữ cái đầu tiên có chữ viết thường. đôi khi trường hợp lạc đà có mũ ban đầu. đôi khi nó không.
masukomi

Sử dụng 2 Regex trong đó bạn chỉ có thể sử dụng một là quá mức cần thiết. Bạn chỉ có thể sử dụng nhóm không chụp:x.gsub(/(?:_|^)(\w)/){$1.upcase}
Ulysse BN

@UlysseBN, và chúng tôi quay lại gsubgiải pháp của bạn , dường như chậm hơn so với mapgiải pháp.
akostadinov

0

Mở rộng chuỗi để thêm Camelize

Trong Ruby thuần túy, bạn có thể mở rộng lớp chuỗi bằng cách sử dụng cùng một mã từ 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.