简体   繁体   English

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

[英]Ruby refactoring: converting array to hash

Here's what I get in Rails params: 这是我在Rails参数中得到的:

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

I have to transform into a following object: 我必须转换为以下对象:

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

Here's the code I have working so far: 到目前为止,这是我正在使用的代码:

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)

Is there a way to refactor it? 有没有办法重构它? Maybe using map . 也许使用map Note that data structure has to change from array to hash as well. 请注意,数据结构也必须从数组更改为哈希。

Thanks for any tips. 感谢您的提示。

Here's one way: 这是一种方法:

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

If you're using Rails you can use the Hash#except method from ActiveSupport to make it a little more succinct: 如果您使用的是Rails,则可以使用ActiveSupport中的Hash#except方法使它更加简洁:

data = obj["raw_data"].map {|item| [ item["id"], item.except("id") ] }
obj2 = { "data" => data.to_h }
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"}}

This special Hash[] syntax lets you create a hash from a array of keys and an array of values. 这种特殊的Hash[]语法使您可以从键数组和值数组创建哈希。

Hash.except(*args) is an ActiveSupport addition to the hash class which returns a new key without the keys in the blacklist. Hash.except(*args)是对哈希类的ActiveSupport补充,它返回一个没有黑名单中的密钥的新密钥。

In rails, you can use index_by method: 在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"}}}

One downfall of this is that it will modify the original data by deleting id property. 其中一个缺点是它将通过删除id属性来修改原始数据。 If this is unacceptable, here is modified, safe version: 如果这是不可接受的,请修改以下安全版本:

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

I assume you mean obj = {...} and not obj => {...} , as the latter is not a valid object. 我假设您的意思是obj = {...}而不是obj => {...} ,因为后者不是有效的对象。 If so: 如果是这样的话:

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

If obj can be mutated, you can simplify a bit: 如果可以对obj进行突变,则可以简化一下:

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

As an improved non-mutating solution, @Max suggested a Rails' tweak: 作为改进的非变异解决方案,@ Max建议使用Rails进行调整:

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

That looks good to me, but as I don't know rails, I'm taking that advice at face value. 这对我来说看起来不错,但是由于我不了解Rails,因此我以表面价值接受该建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM