我如何使用 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 从这个散列中提取值。