代码之家  ›  专栏  ›  技术社区  ›  lacostenycoder

如何在ruby散列上深度转换值

  •  0
  • lacostenycoder  · 技术社区  · 6 年前

    我有一个看起来像这样的杂烩:

    hash = {
      'key1' => ['value'],
      'key2' => {
        'sub1' => ['string'],
        'sub2' => ['string'],
      },
      'shippingInfo' => {
                       'shippingType' => ['Calculated'],
                    'shipToLocations' => ['Worldwide'],
                  'expeditedShipping' => ['false'],
            'oneDayShippingAvailable' => ['false'],
                       'handlingTime' => ['3'],
        }
      }
    

    我需要转换数组中单个字符串的每个值,使其最终如下所示:

    hash = {
      'key1' =>  'value' ,
      'key2' => {
        'sub1' => 'string' ,
        'sub2' => 'string' ,
      },
      'shippingInfo' => {
                       'shippingType' => 'Calculated' ,
                    'shipToLocations' => 'Worldwide' ,
                  'expeditedShipping' => 'false' ,
            'oneDayShippingAvailable' => 'false' ,
                       'handlingTime' => '3' ,
        }
      }
    

    我找到了这个,但没能成功 https://gist.github.com/chris/b4138603a8fe17e073c6bc073eb17785

    1 回复  |  直到 6 年前
        1
  •  3
  •   Sebastián Palma    6 年前

    比如说:

    def deep_transform_values(hash)
      return hash unless hash.is_a?(Hash)
    
      hash.transform_values do |val|
        if val.is_a?(Array) && val.length == 1
          val.first
        else
          deep_transform_values(val)
        end
      end
    end
    

    测试内容如下:

    hash = {
      'key1' => ['value'],
      'key2' => {
        'sub1' => ['string'],
        'sub2' => ['string'],
      },
      'shippingInfo' => {
                       'shippingType' => ['Calculated'],
                    'shipToLocations' => ['Worldwide'],
                  'expeditedShipping' => ['false'],
            'oneDayShippingAvailable' => ['false'],
                       'handlingTime' => ['3'],
                       'an_integer' => 1,
                       'an_empty_array' => [],
                       'an_array_with_more_than_one_elements' => [1,2],
                       'a_symbol' => :symbol,
                       'a_string' => 'string'
        }
      }
    

    给予:

    {
      "key1"=>"value",
      "key2"=>{
        "sub1"=>"string",
        "sub2"=>"string"
      },
      "shippingInfo"=> {
        "shippingType"=>"Calculated",
        "shipToLocations"=>"Worldwide",
        "expeditedShipping"=>"false",
        "oneDayShippingAvailable"=>"false",
        "handlingTime"=>"3",
        "an_integer"=>1,
        "an_empty_array"=>[],
        "an_array_with_more_than_one_elements"=>[1, 2],
        "a_symbol"=>:symbol,
        "a_string"=>"string"
      }
    }
    

    根据你在评论中提出的问题,我想逻辑会有所改变:

    class Hash
      def deep_transform_values
        self.transform_values do |val|
          next(val.first) if val.is_a?(Array) && val.length == 1
          next(val) unless val.respond_to?(:deep_transform_values)
    
          val.deep_transform_values
        end
      end
    end
    
        2
  •  1
  •   ian    6 年前

    另一种方法是,考虑使用对象并允许初始值设定项为您解构一些键。

    很多像我这样的人开始使用ruby来支持perl的原因之一是对象的更好的表达方式取代了数组和散列等原语。好好利用它!

    class ShippingStuff # You've kept the data vague
    
      def initialize key1:, key2:, shippingInfo:
        @blk = -> val {
          val.respond_to?(:push) && val.size == 1 ?
              val.first :
              cleankeys(val)
        }
        @key1 = cleankeys key1
        @key2 = cleankeys key2
        @shippingInfo = shippingInfo
      end
    
      attr_reader :key1, :key2, :shippingInfo
    
      # basically a cut down version of what
      # Sebastian Palma answered with
      def cleankeys data
        if data.respond_to? :transform_values
          data.transform_values &@blk
        else
          @blk.call(data)
        end
      end
    
    end
    
    
    hash = {
      'key1' => ['value'],
      'key2' => {
        'sub1' => ['string'],
        'sub2' => ['string'],
      },
      'shippingInfo' => {
                       'shippingType' => ['Calculated'],
                    'shipToLocations' => ['Worldwide'],
                  'expeditedShipping' => ['false'],
            'oneDayShippingAvailable' => ['false'],
                       'handlingTime' => ['3'],
      }
    }
    
    shipper = ShippingStuff.new hash.transform_keys!(&:to_sym)
    shipper.key1
    # "value"
    shipper.key2
    # {"sub1"=>"string", "sub2"=>"string"}
    shipper.shippingInfo
    # {"shippingType"=>["Calculated"], "shipToLocations"=>["Worldwide"], "expeditedShipping"=>["false"], "oneDayShippingAvailable"=>["false"], "handlingTime"=>["3"]}
    

    同样的道理,我甚至会 Info shippingInfo 数据。

    如果 key1 key2 很有活力,但也有办法( double splat 其中之一)。

        3
  •  1
  •   Cary Swoveland    6 年前
    hash = {
      'key1' => ['value'],
      'key2' => {
        'sub1' => ['string'],
        'sub2' => ['string'],
      },
      'shippingInfo' => {
                       'shippingType' => ['Calculated'],
                    'shipToLocations' => ['Worldwide', 'Web'],
                  'expeditedShipping' => ['false'],
            'oneDayShippingAvailable' => ['false'],
                       'handlingTime' => ['3'],
        }
      }
    

    def recurse(hash)
      hash.transform_values do |v|
        case v
        when Array
          v.size == 1 ? v.first : v
        when Hash
          recurse v
        else
          # raise exception
        end
      end
    end
    

    recurse hash
      #=> {"key1"=>"value",
      #    "key2"=>{
      #      "sub1"=>"string",
      #      "sub2"=>"string"
      #    },
      #    "shippingInfo"=>{
      #      "shippingType"=>"Calculated",
      #      "shipToLocations"=>["Worldwide", "Web"],
      #      "expeditedShipping"=>"false",
      #      "oneDayShippingAvailable"=>"false",
      #      "handlingTime"=>"3"
      #    }
      #  }