我如何使用 ruby 水平转置我的 json 数据
How can i transpose my json data horizontally using ruby
我希望使用 ruby 水平转置我的 json 数据。我的 json 数据看起来像这样
[
{
"ID": "ID001",
"Type": "Type1",
"Week": "W1",
"Count": 1
},
{
"ID": "ID001",
"Type": "Type1",
"Week": "W2",
"Count": 2
},
{
"ID": "ID001",
"Type": "Type2",
"Week": "W1",
"Count": 2
},
{
"ID": "ID001",
"Type": "Type2",
"Week": "W2",
"Count": 3
},
{
"ID": "ID001",
"Type": "Type3",
"Week": "W1",
"Count": 3
},
{
"ID": "ID001",
"Type": "Type3",
"Week": "W2",
"Count": 4
}
]
我的预期结果是这样的
[
{
"ID": "ID001",
"Type": "Type1",
"W1": 1,
"W2": 2
},
{
"ID": "ID001",
"Type": "Type2",
"W1": 2,
"W2": 3
},
{
"ID": "ID001",
"Type": "Type3",
"W1": 3,
"W2": 4
}
]
请告诉我如何用 Ruby 做到这一点。这更多的是通过引用 key Type as group by 将值合并到 key 中的情况。就像我们在 sql 查询中所做的那样
优化功能链中的可读性 non-mutation 样式:
hashes = [
{ ID: "ID001", Type: "Type1", Week: "W1", Count: 1 },
{ ID: "ID001", Type: "Type1", Week: "W2", Count: 2 },
{ ID: "ID001", Type: "Type2", Week: "W1", Count: 2 },
{ ID: "ID001", Type: "Type2", Week: "W2", Count: 3 },
{ ID: "ID001", Type: "Type3", Week: "W1", Count: 3 },
{ ID: "ID001", Type: "Type3", Week: "W2", 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},
# {:ID => "ID001", :Type => "Type3", :W1 => 3, :W2 => 4}]
arr = [
{:ID=>"ID001", :Type=>"Type1", :Week=>"W1", :Count=>1},
{:ID=>"ID001", :Type=>"Type1", :Week=>"W2", :Count=>2},
{:ID=>"ID001", :Type=>"Type2", :Week=>"W1", :Count=>2},
{:ID=>"ID001", :Type=>"Type2", :Week=>"W2", :Count=>3},
{:ID=>"ID001", :Type=>"Type3", :Week=>"W1", :Count=>3},
{:ID=>"ID001", :Type=>"Type3", :Week=>"W2", :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", :Type=>"Type1", :W1=>1, :W2=>2},
# {:ID=>"ID001", :Type=>"Type2", :W1=>2, :W2=>3},
# {:ID=>"ID001", :Type=>"Type3", :W1=>3, :W2=>4}]
参见 Hash#update(a.k.a。merge!
)。
步骤如下
enum = arr.each_with_object({})
#=> #<Enumerator: [{:ID=>"ID001",..., :Count=>2}, {:ID=>"ID001",...,
# :Count=>2},...,{:ID=>"ID001",..., :Count=>4}]:
# each_with_object({})>
第一个元素由enum
生成并传递给块,导致块变量使用Array Decomposition赋值。
g,h = enum.next
#=> [{:ID=>"ID001", :Type=>"Type1", :Week=>"W1", :Count=>1}, {}]
g #=> {:ID=>"ID001", :Type=>"Type1", :Week=>"W1", :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], Type: key, g[:Week].to_sym=>g[:Count])
h["Type1"].update(ID: "ID001", Type: "Type1", "W1".to_sym=>1)
{}.update(ID: "ID001", Type: "Type1", :W1=>1)
#=> {:ID=>"ID001", :Type=>"Type1", :W1=>1}
h #=> {:ID=>"ID001", :Type=>"Type1", :W1=>1}
然后下一个元素由 enum
生成并传递给块。
g,h = enum.next
#=> [{:ID=>"ID001", :Type=>"Type1", :Week=>"W2", :Count=>2},
# {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1}}
g #=> {:ID=>"ID001", :Type=>"Type1", :Week=>"W2", :Count=>2}
h #=> {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1}
key = g[:Type]
#=> "Type1"
h[key] ||= {}
h["Type1"] = h["Type1"] || {}
h["Type1"] = {:ID=>"ID001", :Type=>"Type1", :W1=>1} || {}
h["Type1"] = {:ID=>"ID001", :Type=>"Type1", :W1=>1}
h #=> {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1}} (no change)
h[key].update(ID: g[:ID], Type: key, g[:Week]=>g[:Count])
h["Typt1"].update(ID: g[:ID], Type: key, g[:Week].to_sym=>g[:Count])
{:ID=>"ID001", :Type=>"Type1", "W1"=>1}}.update(ID: g[:ID], Type: key,
g[:Week].to_sym=>g[:Count])
#=> {:ID=>"ID001", :Type=>"Type1", :W1=>1, :W2=>2}
h #=> {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1, :W2=>2}}
对于由 enum
生成并传递给区块的剩余四个元素中的每一个,计算都是相似的,之后 enum
returns 哈希:
{"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1, :W2=>2},
"Type2"=>{:ID=>"ID001", :Type=>"Type2", :W1=>2, :W2=>3},
"Type3"=>{:ID=>"ID001", :Type=>"Type3", :W1=>3, :W2=>4}}
最后一步是使用 Hash#values 从这个散列中提取值。
我希望使用 ruby 水平转置我的 json 数据。我的 json 数据看起来像这样
[
{
"ID": "ID001",
"Type": "Type1",
"Week": "W1",
"Count": 1
},
{
"ID": "ID001",
"Type": "Type1",
"Week": "W2",
"Count": 2
},
{
"ID": "ID001",
"Type": "Type2",
"Week": "W1",
"Count": 2
},
{
"ID": "ID001",
"Type": "Type2",
"Week": "W2",
"Count": 3
},
{
"ID": "ID001",
"Type": "Type3",
"Week": "W1",
"Count": 3
},
{
"ID": "ID001",
"Type": "Type3",
"Week": "W2",
"Count": 4
}
]
我的预期结果是这样的
[
{
"ID": "ID001",
"Type": "Type1",
"W1": 1,
"W2": 2
},
{
"ID": "ID001",
"Type": "Type2",
"W1": 2,
"W2": 3
},
{
"ID": "ID001",
"Type": "Type3",
"W1": 3,
"W2": 4
}
]
请告诉我如何用 Ruby 做到这一点。这更多的是通过引用 key Type as group by 将值合并到 key 中的情况。就像我们在 sql 查询中所做的那样
优化功能链中的可读性 non-mutation 样式:
hashes = [
{ ID: "ID001", Type: "Type1", Week: "W1", Count: 1 },
{ ID: "ID001", Type: "Type1", Week: "W2", Count: 2 },
{ ID: "ID001", Type: "Type2", Week: "W1", Count: 2 },
{ ID: "ID001", Type: "Type2", Week: "W2", Count: 3 },
{ ID: "ID001", Type: "Type3", Week: "W1", Count: 3 },
{ ID: "ID001", Type: "Type3", Week: "W2", 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},
# {:ID => "ID001", :Type => "Type3", :W1 => 3, :W2 => 4}]
arr = [
{:ID=>"ID001", :Type=>"Type1", :Week=>"W1", :Count=>1},
{:ID=>"ID001", :Type=>"Type1", :Week=>"W2", :Count=>2},
{:ID=>"ID001", :Type=>"Type2", :Week=>"W1", :Count=>2},
{:ID=>"ID001", :Type=>"Type2", :Week=>"W2", :Count=>3},
{:ID=>"ID001", :Type=>"Type3", :Week=>"W1", :Count=>3},
{:ID=>"ID001", :Type=>"Type3", :Week=>"W2", :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", :Type=>"Type1", :W1=>1, :W2=>2},
# {:ID=>"ID001", :Type=>"Type2", :W1=>2, :W2=>3},
# {:ID=>"ID001", :Type=>"Type3", :W1=>3, :W2=>4}]
参见 Hash#update(a.k.a。merge!
)。
步骤如下
enum = arr.each_with_object({})
#=> #<Enumerator: [{:ID=>"ID001",..., :Count=>2}, {:ID=>"ID001",...,
# :Count=>2},...,{:ID=>"ID001",..., :Count=>4}]:
# each_with_object({})>
第一个元素由enum
生成并传递给块,导致块变量使用Array Decomposition赋值。
g,h = enum.next
#=> [{:ID=>"ID001", :Type=>"Type1", :Week=>"W1", :Count=>1}, {}]
g #=> {:ID=>"ID001", :Type=>"Type1", :Week=>"W1", :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], Type: key, g[:Week].to_sym=>g[:Count])
h["Type1"].update(ID: "ID001", Type: "Type1", "W1".to_sym=>1)
{}.update(ID: "ID001", Type: "Type1", :W1=>1)
#=> {:ID=>"ID001", :Type=>"Type1", :W1=>1}
h #=> {:ID=>"ID001", :Type=>"Type1", :W1=>1}
然后下一个元素由 enum
生成并传递给块。
g,h = enum.next
#=> [{:ID=>"ID001", :Type=>"Type1", :Week=>"W2", :Count=>2},
# {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1}}
g #=> {:ID=>"ID001", :Type=>"Type1", :Week=>"W2", :Count=>2}
h #=> {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1}
key = g[:Type]
#=> "Type1"
h[key] ||= {}
h["Type1"] = h["Type1"] || {}
h["Type1"] = {:ID=>"ID001", :Type=>"Type1", :W1=>1} || {}
h["Type1"] = {:ID=>"ID001", :Type=>"Type1", :W1=>1}
h #=> {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1}} (no change)
h[key].update(ID: g[:ID], Type: key, g[:Week]=>g[:Count])
h["Typt1"].update(ID: g[:ID], Type: key, g[:Week].to_sym=>g[:Count])
{:ID=>"ID001", :Type=>"Type1", "W1"=>1}}.update(ID: g[:ID], Type: key,
g[:Week].to_sym=>g[:Count])
#=> {:ID=>"ID001", :Type=>"Type1", :W1=>1, :W2=>2}
h #=> {"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1, :W2=>2}}
对于由 enum
生成并传递给区块的剩余四个元素中的每一个,计算都是相似的,之后 enum
returns 哈希:
{"Type1"=>{:ID=>"ID001", :Type=>"Type1", :W1=>1, :W2=>2},
"Type2"=>{:ID=>"ID001", :Type=>"Type2", :W1=>2, :W2=>3},
"Type3"=>{:ID=>"ID001", :Type=>"Type3", :W1=>3, :W2=>4}}
最后一步是使用 Hash#values 从这个散列中提取值。