问题描述
我希望使用ruby水平转置json数据。我的json数据看起来像这样
// to download a file we don't want to use window.location.assign because in case of error the broswer
// will redirect to the error page,instead we create an invisible iframe and set src to it
function downloadToFile(url) {
let iframe = document.getElementById('downloadFileIframe');
if (!iframe) {
iframe = document.createElement('iframe');
iframe.setAttribute('id','downloadFileIframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
iframe.setAttribute('src',url);
}
我的预期结果是这样的
[
{
"ID": "ID001","Type": "Type1","Week": "W1","Count": 1
},{
"ID": "ID001","Week": "W2","Count": 2
},"Type": "Type2","Count": 3
},"Type": "Type3","Count": 4
}
]
请让我知道如何使用Ruby做到这一点。这更多是通过将键类型作为分组依据来将值合并到键中的情况。就像我们在sql查询中所做的一样
解决方法
以功能性链接非突变形式优化可读性:
hashes = [
{ ID: "ID001",Type: "Type1",Week: "W1",Count: 1 },{ ID: "ID001",Week: "W2",Count: 2 },Type: "Type2",Count: 3 },Type: "Type3",Count: 4 }
]
grouped = hashes.group_by do |hash|
hash.slice(:ID,:Type)
end
values_transposed = grouped.transform_values do |grp|
grp.map { |h| [h[:Week].to_sym,h[:Count]] }.to_h
end
values_transposed.map { |grp,values| grp.merge(values) }
# => [
# {:ID => "ID001",:Type => "Type1",:W1 => 1,:W2 => 2},# {:ID => "ID001",:Type => "Type2",:W1 => 2,:W2 => 3},:Type => "Type3",:W1 => 3,:W2 => 4}]
,
arr = [
{:ID=>"ID001",:Type=>"Type1",:Week=>"W1",:Count=>1},{:ID=>"ID001",:Week=>"W2",:Count=>2},:Type=>"Type2",:Count=>3},:Type=>"Type3",:Count=>4}]
]
arr.each_with_object({}) do |g,h|
key = g[:Type]
(h[key] ||= {}).update(ID: g[:ID],Type: key,g[:Week].to_sym=>g[:Count])
end.values
#=> [{:ID=>"ID001",:W1=>1,:W2=>2},# {:ID=>"ID001",:W1=>2,:W2=>3},:W1=>3,:W2=>4}]
请参见Hash#update(又称merge!
)。
步骤如下。
enum = arr.each_with_object({})
#=> #<Enumerator: [{:ID=>"ID001",...,# :Count=>2},:Count=>4}]:
# each_with_object({})>
第一个元素由enum
生成并传递给块,从而使块变量使用Array Decomposition赋值。
g,h = enum.next
#=> [{:ID=>"ID001",{}]
g #=> {:ID=>"ID001",:Count=>1}
h #=> {}
然后执行块计算。
key = g[:Type]
#=> "Type1"
h[key] ||= {}
h["Type1"] = h["Type1"] || {}
h["Type1"] = nil || {}
h["Type1"] = {}
h #=> { "Type1"=>{} }
h[key].update(ID: g[:ID],g[:Week].to_sym=>g[:Count])
h["Type1"].update(ID: "ID001","W1".to_sym=>1)
{}.update(ID: "ID001",:W1=>1)
#=> {:ID=>"ID001",:W1=>1}
h #=> {:ID=>"ID001",:W1=>1}
然后,下一个元素由enum
生成并传递到该块。
g,# {"Type1"=>{:ID=>"ID001",:W1=>1}}
g #=> {:ID=>"ID001",:Count=>2}
h #=> {"Type1"=>{:ID=>"ID001",:W1=>1}
key = g[:Type]
#=> "Type1"
h[key] ||= {}
h["Type1"] = h["Type1"] || {}
h["Type1"] = {:ID=>"ID001",:W1=>1} || {}
h["Type1"] = {:ID=>"ID001",:W1=>1}
h #=> {"Type1"=>{:ID=>"ID001",:W1=>1}} (no change)
h[key].update(ID: g[:ID],g[:Week]=>g[:Count])
h["Typt1"].update(ID: g[:ID],g[:Week].to_sym=>g[:Count])
{:ID=>"ID001","W1"=>1}}.update(ID: g[:ID],g[:Week].to_sym=>g[:Count])
#=> {:ID=>"ID001",:W2=>2}
h #=> {"Type1"=>{:ID=>"ID001",:W2=>2}}
对于enum
生成并传递到块的其余四个元素,每个元素的计算都是相似的,之后enum
返回哈希值:
{"Type1"=>{:ID=>"ID001","Type2"=>{:ID=>"ID001","Type3"=>{:ID=>"ID001",:W2=>4}}
最后一步是使用Hash#values从此哈希中提取值。