如何深入转换Ruby哈希值

问题描述 投票:1回答:3

我有一个哈希,看起来像这样:

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

ruby hash transform
3个回答
3
投票

怎么样的:

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

1
投票

作为替代方案,请考虑使用对象并允许初始化程序为您解构某些键。

许多像我这样的人开始使用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类。

如果key1key2是动态的,你可能会遇到一个不同的问题,但也有办法绕过它(double splat为一个)。


1
投票
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"
  #    }
  #  } 
© www.soinside.com 2019 - 2024. All rights reserved.