2016-06-28 42 views
3

我想將嵌套散列中的所有值轉換爲與utf8兼容的字符串。我最初認爲這很容易,並且應該可以使用類似deep_apply的內容,但我無法在快速Google和SO搜索中找到任何此類簡單內容。如何轉換嵌套散列中的所有值?

我不想寫(維護)與Change values in a nested hash相似的方法。是否有一個本地API實現或速記可用於此,還是我必須寫我自己的方法?

回答

2

有趣的是學習了「The F」回答中採用的deep_merge方法。這是另一種需要添加一些輔助方法的方法。

首先,輔助方法:

從頂端回答here (converting-a-nested-hash-into-a-flat-hash)

def flat_hash(h,f=[],g={}) 
    return g.update({ f=>h }) unless h.is_a? Hash 
    h.each { |k,r| flat_hash(r,f+[k],g) } 
    g 
end 

從名爲ruby-bury一個GitHub庫(此功能提出了到Ruby核心,但rejected

class Hash 
    def bury *args 
    if args.count < 2 
     raise ArgumentError.new("2 or more arguments required") 
    elsif args.count == 2 
     self[args[0]] = args[1] 
    else 
     arg = args.shift 
     self[arg] = {} unless self[arg] 
     self[arg].bury(*args) unless args.empty? 
    end 
    self 
    end 
end 

然後一個方法綁一起:

def change_all_values(hash, &blk)  
    # the next line makes the method "pure functional" 
    # but can be removed otherwise. 
    hash = Marshal.load(Marshal.dump(hash)) 

    flat_hash(hash).each { |k,v| hash.bury(*(k + [blk.call(v)])) } 
    hash 
end 

的利用方法:

irb(main):063:0> a = {a: 1, b: { c: 1 } } 
=> {:a=>1, :b=>{:c=>1}} 
irb(main):064:0> b = change_all_values(a) { |val| val + 1 } 
=> {:a=>2, :b=>{:c=>2}} 
irb(main):066:0> a 
=> {:a=>1, :b=>{:c=>1}} 
+0

尼斯.. !!!我最終從你的例子中獲得了靈感,並實現了我自己的類似但更小的方法,它適用於我的用例。非常感謝。 – Vedanshu

2

deep_merge

yourhash.deep_merge(yourhash) {|_,_,v| v.to_s} 

合併哈希與自身,檢查值,就可以調用to_s
如果您不在rails上使用ruby,則此方法需要文件頂部的require 'active_support/core_ext/hash'

顯然,您可以按照您的要求處理deep_mergev的轉換,以滿足您的要求。

在軌控制檯:

2.3.0 :001 > h1 = { a: true, b: { c: [1, 2, 3] } } 
=> {:a=>true, :b=>{:c=>[1, 2, 3]}} 

2.3.0 :002 > h1.deep_merge(h1) { |_,_,v| v.to_s} 
=> {:a=>"true", :b=>{:c=>"[1, 2, 3]"}} 
1

嗯,這是很簡單的寫吧 - 那麼,爲什麼不寫你自己的和絕對確保它是如何在各種情況下的行爲;)

def to_utf8(h) 
    if h.is_a? String 
     return h.force_encoding('utf-8') 
    elsif h.is_a? Symbol 
     return h.to_s.force_encoding('utf-8').to_sym 
    elsif h.is_a? Numeric 
     return h 
    elsif h.is_a? Array 
     return h.map { |e| to_utf8(e) }.to_s 
    else 
     return h.to_s.force_encoding('utf-8') 
    end 
    return hash.to_a.map { |e| result.push(to_utf8(e[0], e[1])) }.to_h 
end 

您可能想檢查所有行爲和轉換是否正確 - 並在必要時進行更改。

3

我最終實現我自己的方法,那就是絕不完美,但很適合我的使用情況和應易於維護。在這裏張貼供參考的人誰願意嘗試一下

def deep_apply object, klasses, &blk 
    if object.is_a? Array 
     object.map { |obj_ele| deep_apply(obj_ele, klasses, &blk) } 
    elsif object.is_a? Hash 
     object.update(object) {|_, value| deep_apply(value, klasses, &blk) } 
    elsif klasses.any? { |klass| object.is_a? klass } 
     blk.call(object) 
    else 
     object 
    end 
    end 

用法:

=> pry(main)> deep_apply({a: [1, 2, "sadsad"]}, [String, Integer]) { |v| v.to_s + "asd" } 
=> {:a=>["1asd", "2asd", "sadsadasd"]}