2012-01-03 28 views
19

Tôi đang cố gắng xây dựng một gem wrapper API, và có vấn đề với việc chuyển đổi các khóa băm sang một định dạng Rubyish hơn từ JSON API trả về.Chuyển đổi các khóa băm lồng nhau từ CamelCase sang snake_case trong Ruby

JSON chứa nhiều lớp lồng nhau, cả Hashes và Arrays. Những gì tôi muốn làm là chuyển đổi đệ quy tất cả các khóa thành snake_case để sử dụng dễ dàng hơn.

Đây là những gì tôi đã có cho đến nay:

def convert_hash_keys(value) 
    return value if (not value.is_a?(Array) and not value.is_a?(Hash)) 
    result = value.inject({}) do |new, (key, value)| 
    new[to_snake_case(key.to_s).to_sym] = convert_hash_keys(value) 
    new 
    end 
    result 
end 

Các cuộc gọi trên phương pháp này để chuyển đổi chuỗi để snake_case:

def to_snake_case(string) 
    string.gsub(/::/, '/'). 
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). 
    gsub(/([a-z\d])([A-Z])/,'\1_\2'). 
    tr("-", "_"). 
    downcase 
end 

Lý tưởng nhất, kết quả sẽ tương tự như sau:

hash = {:HashKey => {:NestedHashKey => [{:Key => "value"}]}} 

convert_hash_keys(hash) 
# => {:hash_key => {:nested_hash_key => [{:key => "value"}]}} 

Tôi nhận được lỗi đệ quy và mọi phiên bản của loại giải pháp này mà tôi đã thử không chuyển đổi các biểu tượng vượt quá mức đầu tiên hoặc đi quá mức và cố gắng chuyển đổi toàn bộ băm, bao gồm các giá trị.

Cố gắng giải quyết tất cả điều này trong lớp trợ giúp, thay vì sửa đổi các hàm băm và chuỗi thực tế, nếu có thể.

Cảm ơn bạn trước.

+0

Trước khi làm bất cứ điều gì khác, 'if (không ... và không ...)' là một nơi hoàn hảo để sử dụng luật của De Morgan. Bạn nên viết nó 'trừ khi ... hoặc ... '. – sawa

Trả lời

31

Bạn cần phải xử lý riêng biệt Array và Hash. Và, nếu bạn đang ở trong Rails, bạn có thể sử dụng underscore thay vì homebrew của bạn to_snake_case. Lần đầu tiên một helper ít để giảm tiếng ồn:

def underscore_key(k) 
    k.to_s.underscore.to_sym 
    # Or, if you're not in Rails: 
    # to_snake_case(k.to_s).to_sym 
end 

Nếu Hashes của bạn sẽ có chìa khóa mà không phải là biểu tượng hoặc Strings sau đó bạn có thể sửa đổi underscore_key một cách thích hợp.

Nếu bạn có một mảng, sau đó bạn chỉ muốn đệ quy áp dụng convert_hash_keys cho mỗi phần tử của mảng; nếu bạn có một Hash, bạn muốn sửa các phím với underscore_key và áp dụng convert_hash_keys cho mỗi giá trị; nếu bạn có một cái gì đó khác sau đó bạn muốn vượt qua nó thông qua ảnh hưởng:

def convert_hash_keys(value) 
    case value 
    when Array 
     value.map { |v| convert_hash_keys(v) } 
     # or `value.map(&method(:convert_hash_keys))` 
    when Hash 
     Hash[value.map { |k, v| [underscore_key(k), convert_hash_keys(v)] }] 
    else 
     value 
    end 
end 
+0

Hoạt động như một sự quyến rũ. Cám ơn sự tử tế của anh. Tôi không làm điều này trong Rails, nhưng tôi tin rằng mã tôi sử dụng cho 'to_snake_case' xuất phát từ phương thức Rails' underscore'. –

+0

@Andrew: 'string.gsub (/ :: /, '/')' trong 'to_snake_case' khiến tôi nghĩ bạn đúng về nơi' to_snake_case' xuất phát từ đó. Chào mừng bạn đến với SO, tận hưởng kỳ nghỉ của bạn. –

+0

Không cần thiết phải sử dụng Rails, chỉ cần ActiveSupport, cho phép chúng tôi chọn lựa thói quen. 'require 'active_support/core_ext/string/inflections'' nên làm điều đó:'' FooBar'.underscore => "foo_bar" '. –

17

Nếu bạn sử dụng Rails:

Ví dụ với băm: camelCase để snake_case:

hash = { camelCase: 'value1', changeMe: 'value2' } 

hash.transform_keys { |key| key.to_s.underscore } 
# => { "camel_case" => "value1", "change_me" => "value2" } 

nguồn: http://apidock.com/rails/v4.0.2/Hash/transform_keys

Đối với thuộc tính lồng nhau sử dụng deep_transform_keys thay vì transform_keys, ví dụ:

hash = { camelCase: 'value1', changeMe: { hereToo: { andMe: 'thanks' } } } 

hash.deep_transform_keys { |key| key.to_s.underscore } 
# => {"camel_case"=>"value1", "change_me"=>{"here_too"=>{"and_me"=>"thanks"}}} 

nguồn: http://apidock.com/rails/v4.2.7/Hash/deep_transform_keys

+0

điều này chỉ thay đổi mức đầu tiên của băm, các khóa băm lồng nhau vẫn được giữ nguyên như trường hợp lạc đà. – nayiaw

+1

Cảm ơn sự chú ý Tôi đã cập nhật câu trả lời của mình. –

+0

Hoạt động như một sự quyến rũ, cảm ơn! – born4new

0

Nếu bạn đang sử dụng thư viện active_support, bạn có thể sử dụng deep_transform_keys! như vậy:

hash.deep_transform_keys! do |key| 
    k = key.to_s.snakecase rescue key 
    k.to_sym rescue key 
end 
Các vấn đề liên quan