2016-06-28 27 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"]}