2012-01-03 39 views
19

APIラッパーの宝石を作成しようとしています。APIが返すJSONからハッシュキーをRuby形式に変換する際に問題があります。CamelCaseのネストされたハッシュキーをRubyのsnake_caseに変換する

JSONには、ハッシュと配列の両方の複数のネスティングレイヤが含まれています。私がやりたいことは、簡単に使用できるようにすべてのキーをsnake_caseに再帰的に変換することです。ここで

は、私がこれまで持っているものです:

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 

上記の呼び出しが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 

理想的には、結果は以下のようになります。

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

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

再帰が間違っています。この種類のソリューションのすべてのバージョンを試してみました。最初のレベルを超えてシンボルを変換しない、または船外に出て値を含むハッシュ全体を変換しようとします。

可能であれば、実際のハッシュ関数と文字列関数を変更するのではなく、ヘルパークラスでこれをすべて解決しようとしています。

ありがとうございます。

+0

他に何かをする前に、 'if(not ...とnot ...)'はDe Morganの法則を使うのに最適な場所です。あなたは '...または... ... 'と書かなければなりません。 – sawa

答えて

31

ArrayとHashを別々に扱う必要があります。また、Railsの場合は、自家製のto_snake_caseの代わりにunderscoreを使用できます。ノイズを低減するために最初の小さなヘルパー:

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 

あなたのハッシュは記号や文字列でないキーがあるとすれば、あなたは適切underscore_keyを変更することができます。

アレイを使用している場合は、配列の各要素に再帰的にconvert_hash_keysを適用したいだけです。ハッシュがある場合は、キーをunderscore_keyに固定し、それぞれの値にconvert_hash_keysを適用します。あなたが何かを持っているならば、あなたはそのままを通してそれを渡したい:

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

魅力的な作品です。親切にありがとう。私はRailsでこれをやっていませんが、 'to_snake_case'に使用したコードはRailsの' underscore'メソッドから来ていると思います。 –

+0

@Andrew: 'to_snake_case'の' string.gsub(/ :: /、/ '/ ')は' to_snake_case'がどこから来たのかあなたが正しいと思います。ようこそ、あなたの滞在をお楽しみください。 –

+0

Railsを使う必要はありません。ActiveSupportだけで、ルーチンをチェリーピックアップできます。 'require 'active_support/core_ext/string/inflections''llはそれを行うべきです:' 'FooBar'.underscore =>" foo_bar "'。 –

17

あなたはRailsのを使用する場合は

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

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

ソース:ネストされた属性について http://apidock.com/rails/v4.0.2/Hash/transform_keys

ではなくtransform_keysのdeep_transform_keysを使用し、例:

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

ソース:あなたはactive_supportライブラリを使用している場合http://apidock.com/rails/v4.2.7/Hash/deep_transform_keys

+0

これはハッシュの第1レベルのみを変換し、ネストされたハッシュキーはまだラクダのように残っています。 – nayiaw

+1

私の答えを更新しました。 –

+0

魅力のように動作します、ありがとう! – born4new

0

、あなたはdeep_transform_keysを使用することができます!

hash.deep_transform_keys! do |key| 
    k = key.to_s.snakecase rescue key 
    k.to_sym rescue key 
end 
関連する問題