【问题标题】:How to trasform all values in a nested hash?如何转换嵌套哈希中的所有值?
【发布时间】:2016-06-29 02:47:22
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    我最终实现了自己的方法,这绝不是完美的,但适用于我的用例并且应该易于维护。在这里发布以供任何想尝试的人参考

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

    【讨论】:

      【解决方案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】:

      deep_merge

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

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

      显然,您可以根据需要在deep_merge 中处理v 的转换。

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

      【讨论】:

        【解决方案4】:

        嗯,写起来很简单——所以为什么不自己写,并绝对确定它在所有情况下的表现;)

        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
        

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-03-10
          • 1970-01-01
          • 1970-01-01
          • 2020-01-30
          • 2018-11-08
          • 1970-01-01
          • 2019-03-21
          相关资源
          最近更新 更多