将哈希更改为哈希数组

Changing a hash to an array of hash

我想将这个 ruby 哈希结构转换为一个哈希数组。结束结构将在图表中使用 api,提前致谢

发件人:

data = 
    {
        ["Transportations", "Not-Approved"] => 350,
        ["Transportations", "Approved"]     => 160,
        ["Communications",  "Not-Approved"] => 300,
        ["Communications","Approved"]       => 80,
        ["Rentals","Not-Approved"]          => 50,
        ["Rentals", "Approved"]             => 145,
        ["Salaries","Not-Approved"]         => 150,
        ["Salaries", "Approved"]            => 310
    }

收件人:

data = [
    {
        name: "Transportations",
        data: [["Not-Approved", 350], ["Approved", 160]]
    },
    {
        name: "Communications",
        data: [["Not-Approved", 300], ["Approved", 80]]
    },
    {
        name: "Rentals",
        data: [["Not-Approved", 50], ["Approved", 145]]
    },
    {
        name: "Salaries",
        data: [["Not-Approved", 150], ["Approved", 310]]
    }
]

试试这个,

h = Hash.new { |h, k| h[k] = { name: k, data: {} }}
data.each { |(name, each), value| h[name][:data][each] = value };
h.values.each { |each| each[:data] = each[:data].to_a }
array = h.values

代码

def rearrange(data)
  data.group_by { |k,_| k.first }.map { |name, arr|
    { name: name, data: arr.map { |(_,outcome), val| [outcome, val] } } }
end

例子

data = {
  ["Transportations", "Not-Approved"] => 350,
  ["Transportations", "Approved"]     => 160,
  ["Communications",  "Not-Approved"] => 300,
  ["Communications","Approved"]       => 80,
  ["Rentals","Not-Approved"]          => 50,
  ["Rentals", "Approved"]             => 145,
  ["Salaries","Not-Approved"]         => 150,
  ["Salaries", "Approved"]            => 310
}

rearrange(data)
  #=> [{:name=>"Transportations",
  #     :data=>[["Not-Approved", 350], ["Approved", 160]]},
  #    {:name=>"Communications",
  #     :data=>[["Not-Approved", 300], ["Approved", 80]]},
  #    {:name=>"Rentals",
  #     :data=>[["Not-Approved", 50], ["Approved", 145]]},
  #    {:name=>"Salaries",
  #     :data=>[["Not-Approved", 150], ["Approved", 310]]}
  #   ] 

说明

第一步如下

h = data.group_by { |k,_| k.first }
  #=> { "Transportations"=>[
  #       [["Transportations", "Not-Approved"], 350],
  #       [["Transportations", "Approved"], 160]
  #     ],
  #     "Communications"=>[
  #       [["Communications", "Not-Approved"], 300],
  #       [["Communications", "Approved"], 80]
  #     ],
  #     "Rentals"=>[
  #       [["Rentals", "Not-Approved"], 50],
  #       [["Rentals", "Approved"], 145]
  #     ],
  #     "Salaries"=>[
  #       [["Salaries", "Not-Approved"], 150],
  #       [["Salaries", "Approved"], 310]
  #     ]
  #   } 

Enumerable#group_by 的第二个块变量保存键的值(键是第一个块变量)。例如,当键为 ["Transportations", "Not-Approved"] 时,值为 350。我把block变量_(一个有效的局部变量)赋值给了值,主要是通知reader在block计算中没有用到

要理解第二步,h的键值对的映射,先定义

enum = h.map
  #=> #<Enumerator:
  #     {"Transportations"=>[
  #        [["Transportations", "Not-Approved"], 350],
  #        [["Transportations", "Approved"], 160]
  #      ], "Communications"=>[
  #    ...
  #          [["Salaries", "Approved"], 310]]}:map>

生成enum的第一个元素并传递给块,块变量使用并行赋值.

赋值
name, arr = enum.next
  #=> ["Transportations", [
  #      [["Transportations", "Not-Approved"], 350],
  #      [["Transportations", "Approved"], 160]
  #    ]
  #   ] 
name
  #=> "Transportations" 
arr
  #=> [
  #    [["Transportations", "Not-Approved"], 350],
  #    [["Transportations", "Approved"], 160]
  #   ]
然后进行

map的分块计算。首先计算

a = arr.map { |(_,outcome), val| [outcome, val] }
  #=> [["Not-Approved", 350], ["Approved", 160]]

然后构造`"Transportation".

的散列
{ name: name, data: a }
  #=> {:name=>"Transportations",
  #    :data=>[["Not-Approved", 350], ["Approved", 160]]} 

其余计算类似。