[英]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.