如何在嵌套哈希中转换所有值?

时间:2016-06-28 06:01:42

标签: ruby-on-rails ruby ruby-on-rails-4

我想将嵌套哈希中的所有值转换为utf8兼容字符串。我最初认为这很容易,我应该可以使用deep_apply之类的东西,但是我无法在快速谷歌和搜索中找到任何这么简单的东西。

我不想编写(维护)类似Change values in a nested hash行的方法。是否有本机API实现或可用于此的简写,还是我必须编写自己的方法?

4 个答案:

答案 0 :(得分: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"]}

答案 1 :(得分:2)

deep_merge

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

将哈希值与自身合并,检查值并在其上调用to_s 如果您没有在rails上使用ruby,则此方法在文件顶部需要require 'active_support/core_ext/hash'

显然,您可以根据自己的要求处理vdeep_merge的转换。

在rails控制台中:

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

答案 2 :(得分: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}}

答案 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

您可能想要检查所有行为和转化是否正确 - 并在必要时进行更改。