Ruby重构:将数组转换为哈希

时间:2016-02-24 19:34:03

标签: ruby-on-rails ruby

这是我在Rails params中获得的内容:

obj => {
    "raw_data" =>
        [
            { "id" => "1", "name" => "John Doe" },
            { "id" => "2", "name" => "Jane Doe" }
        ]
}

我必须转换成以下对象:

obj => {
    "data" =>
        {
            "1" => { "name" => "John Doe" },
            "2" => { "name" => "Jane Doe" }
        }
}

这是我目前工作的代码:

if obj[:raw_data]
    obj[:data] = Hash.new
    obj[:raw_data].each do |raw|
      obj[:data][raw[:id]] = Hash.new
      obj[:data][raw[:id]][:name] = raw[:name] if raw[:name].present?
    end
end
obj.delete(:raw_data)

有没有办法重构它?也许使用map。请注意,数据结构也必须从数组更改为哈希。

感谢您的任何提示。

4 个答案:

答案 0 :(得分:2)

这是一种方式:

obj = {
  "raw_data" => [
    { "id" => "1", "name" => "John Doe" },
    { "id" => "2", "name" => "Jane Doe" }
  ]
}

data = obj["raw_data"].map do |item|
  item = item.dup
  [ item.delete('id'), item ]
end

obj2 = { "data" => data.to_h }
# => { "data" =>
#      { "1" => { "name" => "John Doe" },
#        "2" => { "name" => "Jane Doe" }
#      }
#    }

如果您正在使用Rails,可以使用ActiveSupport中的Hash#except方法使其更简洁:

data = obj["raw_data"].map {|item| [ item["id"], item.except("id") ] }
obj2 = { "data" => data.to_h }

答案 1 :(得分:1)

d = obj[:raw_data]
keys = d.map { |h| h["id"] }
values = d.map { |h| h.except("id") }
Hash[ keys.zip(values) ]

# or as a oneliner
Hash[ d.map { |h| h["id"] }.zip(d.map { |h| h.except("id")) ]
# => {"1"=>{"name"=>"John Doe"}, "2"=>{"name"=>"Jane Doe"}}

这种特殊的Hash[]语法允许您从键数组和值数组创建哈希。

Hash.except(*args)是散列类的ActiveSupport添加,它返回一个没有黑名单中的键的新密钥。

答案 2 :(得分:0)

在rails中,您可以使用index_by方法:

obj = {raw_data: [{id: "1", name: "John Doe"}, {id: "2", name: "Jane Doe"}]}

obj2 = {
 data: obj[:raw_data].index_by {|h| h[:id]}.each {|_,h| h.delete(:id)}
} #=> {:data=>{"1"=>{:name=>"John Doe"}, "2"=>{:name=>"Jane Doe"}}}

这样做的一个缺点就是它会通过删除id属性来修改原始数据。如果这是不可接受的,这里有修改,安全版本:

obj2 = {
 data: obj[:raw_data].map(&:clone).index_by {|h| h[:id]}.each {|_,h| h.delete(:id)}
} #=> {:data=>{"1"=>{:name=>"John Doe"}, "2"=>{:name=>"Jane Doe"}}}

答案 3 :(得分:0)

我认为你的意思是obj = {...}而不是obj => {...},因为后者不是有效的对象。如果是这样的话:

{ "data" => obj["raw_data"].each_with_object({}) { |g,h|
  h[g["id"]] = g.reject { |k,_| k == "id" } } }
  #=> {"data"=>{"1"=>{"name"=>"John Doe"}, "2"=>{"name"=>"Jane Doe"}}}

如果obj可以变异,你可以简化一下:

{ "data" => obj["raw_data"].each_with_object({}) { |g,h| h[g.delete("id")]=g } }

作为一种改进的非变异解决方案,@ Max提出了一个Rails'调整:

{ "data" => obj["raw_data"].each_with_object({}) { |g,h| h[g["id"]] = g.except("id") } }

这对我来说很好看,但由于我不了解铁轨,我从表面上看这个建议。