2013-06-08 4 views
2

Данный хэш с вложенными документами:как изменить хэш-ключи в нижнем регистре, используя Руби

myHash = { 
    "MemberId"=>"ABC0001", 
    "MemberName"=>"Alan", 
    "details"=>[ 
    {"LineNumber"=>"4.1", "Item"=>"A0001", "Description"=>"Apple"}, 
    {"LineNumber"=>"5.1", "Item"=>"A0002"}, 
    {"LineNumber"=>"6.1", "Item"=>"Orange"} 
    ] 
} 

Я хочу изменить его так, он будет выглядеть следующим образом:

{ 
    "memberid"=>"ABC0001", 
    "membername"=>"Alan", 
    "details"=>[ 
    {"linenumber"=>"4.1", "item"=>"A0001", "description"=>"Apple"}, 
    {"linenumber"=>"5.1", "item"=>"A0002"}, 
    {"linenumber"=>"6.1", "item"=>"Orange"} 
    ] 
} 

Другими словами, я хотите изменить на нижний регистр, если он есть в хэш-ключе. Я понимаю, что мне придется перебирать хэш и использовать downcase метод. Если в рубине есть простой способ сделать это?

ответ

4
class Hash 
    def downcase_key 
    keys.each do |k| 
     store(k.downcase, Array === (v = delete(k)) ? v.map(&:downcase_key) : v) 
    end 
    self 
    end 
end 

myHash.downcase_key 
+0

Что касается @cdshines, этот код не работает, когда ключи сами являются хешем. – ZeDalaye

3
def f h 
    Hash[h.map{|k,v| v.class == Array ? [k,v.map{|r| f r}.to_a] : [k.downcase,v]}] 
end 

proof

+0

Ваш код не работает, когда ключ сам является Hash – ZeDalaye

2

Я бы сначала создать метод на Hash, что позволяет отображать ключи от новых значений:

class Hash 
    def map_keys! &blk 
    keys.each do |k| 
     new_k = blk.call(k) 
     self[new_k] = delete(k) 
    end 
    self 
    end 

    def map_keys &blk 
    dup.map_keys!(&blk) 
    end 
end 

Вы можете теперь downcase первого уровня с

myHash.map_keys!(&:downcase) 

myHash теперь содержит:

{"details"=> 
    [{"LineNumber"=>"4.1", "Item"=>"A0001", "Description"=>"Apple"}, 
    {"LineNumber"=>"5.1", "Item"=>"A0002"}, 
    {"LineNumber"=>"6.1", "Item"=>"Orange"}], 
"memberid"=>"ABC0001", 
"membername"=>"Alan"} 

Вложенные хэши могут быть преобразованы с

myHash['details'].each{|h| h.map_keys!(&:downcase) } 

myHash теперь содержит:

{"details"=> 
    [{"linenumber"=>"4.1", "item"=>"A0001", "description"=>"Apple"}, 
    {"linenumber"=>"5.1", "item"=>"A0002"}, 
    {"linenumber"=>"6.1", "item"=>"Orange"}], 
"memberid"=>"ABC0001", 
"membername"=>"Alan"} 
+0

и теперь просто собирайте все ваши шаги вместе одним способом, и он будет делать;) – tkroman

+1

Я думаю, что писать многоразовый и читаемый код важнее, чем подгонять все в одно- лайнер ;-) –

0

Вот функция, которая рекурсивно спускается в любой произвольно вложенных перечислимых и удаляет любые хеш-ключи, которые он находит:

data = { 
    "MemberId"=>"ABC0001", 
    "MemberName"=>"Alan", 
    "details"=>[ 
    {"LineNumber"=>"4.1", "Item"=>"A0001", "Description"=>"Apple"}, 
    {"LineNumber"=>"5.1", "Item"=>"A0002"}, 
    {"LineNumber"=>"6.1", "Item"=>"Orange"} 
    ] 
} 

def downcase_hash_keys(h) 
    if h.is_a?(Hash) 
    h.keys.each do |key| 
     new_key = key.to_s.downcase 
     h[new_key] = h.delete(key) 
     downcase_hash_keys(h[new_key]) 
    end 
    elsif h.respond_to?(:each) 
    h.each { |e| downcase_hash_keys(e) } 
    end 
    h 
end 

downcase_hash_keys(data) 
p data 


# => {"memberid"=>"ABC0001", "membername"=>"Alan", "details"=>[{"linenumber"=>"4.1", "item"=>"A0001", "description"=>"Apple"}, {"linenumber"=>"5.1", "item"=>"A0002"}, {"linenumber"=>"6.1", "item"=>"Orange"}]} 

Это немного уродливое, потому что оно мутирует структуру данных на месте; в идеале, будет создан новый хеш. Но он достигает желаемого результата.

1

Я нашел это решение во время работы над той же проблемой сегодня.

def lowercase_keys(h) 
    new_h = { } 
    h.each_pair do |k, v| 
    new_k = deep_lowercase(k, true) 
    new_v = deep_lowercase(v, false) 
    new_h[new_k] = new_v 
    end 
    new_h 
end 

def deep_lowercase(object, with_strings) 
    case object 
    when String then 
     with_strings ? object.downcase : object 
    when Hash then 
     lowercase_keys(object) 
    else 
     object.respond_to?(:map) ? object.map { |e| deep_lowercase(e, false) } : object 
    end 
end 

Из моих тестов он работает независимо от того, являются ли Хэш значениями или ключами или встроены в Перечислимый.

Смежные вопросы