将Ruby中的嵌套哈希转换为表格格式

时间:2015-01-13 17:31:01

标签: ruby hash

我在ruby中有一个嵌套的哈希:

{"Table1"=>{"Acct type"=>{"Expected"=>"ACC", "Actual"=>"ACC"}, "Seq No"=>{"Expected"=>"100.0", "Actual"=>#<BigDecimal:56d0b28,'0.1E3',9(18)>}, "Class"=>{"Expected"=>"AC", "Actual"=>"AC"}}, "Table 2"=>{"Date"=>{"Expected"=>"20140606", "Actual"=>"20130606"}}}

我需要以表格格式显示上面的嵌套哈希 -

Table   Field   Expected Value  Actual Value
Table 1 Acct type  ACC            ACC
Table 1 Seq No     100.0          100.0
Table 1 Class      AC             AC
Table 2 Date       20140606       20130606

任何建议/指针都会非常有用。我尝试过使用&#39; tableprint&#39;和&#39; text-table&#39;宝石但却无法获得理想的结果。上述数据是使用ActiveRecord从DB2表中提取的。

2 个答案:

答案 0 :(得分:0)

这是一个解决方案:

x = {
  "Table1"=>{
    "Acct type"=>{"Expected"=>"ACC", "Actual"=>"ACC"},
    "Seq No"=>{"Expected"=>"100.0", "Actual"=> 100.00},
    "Class"=>{"Expected"=>"AC", "Actual"=>"AC"}
  },
  "Table 2"=>{
    "Date"=> {"Expected"=>"20140606",
              "Actual"=>"20130606"}}}

def table_row(row_name, row_values)
  [row_name, row_values["Expected"], row_values["Actual"]]
end

def table_rows(table_hash)
  table_hash.map do |field_name, field_values|
    table_row(field_name, field_values)
  end
end

def widest_string_lengths(visual_table)
  table_columns_count = visual_table.first.size - 1
  0.upto(table_columns_count).map do |index|
    columns = visual_table.map { |row| row[index].to_s }
    columns.max_by(&:length).length + 2
  end
end

def aligned_visual_table(visual_table)
  widest_string_items = widest_string_lengths(visual_table)

  visual_table.map do |row|
    row.each_with_index.map do |cell, index|
      cell.to_s.ljust(widest_string_items[index] + 2)
    end
  end
end

table_headers = ['Table', 'Field', 'Value', 'Actual Value']

# This just turns the hash to array of arrays
visual_table = x.map do |table_name, table_hash|
   table_rows(table_hash).map do |table_rows|
      table_rows.insert(0, table_name)
    end
end.flatten(1).insert(0, table_headers)

puts "*********"
puts aligned_visual_table(visual_table).map(&:join).join("\n")

答案 1 :(得分:0)

您的示例表明存在影响表格外观的未指定参数。例如,列标签“表”和“字段”是左调整的,而“预期值”和“实际值”则不是。此外,“预期值”与“帐户类型”重叠。下面我建议你如何构建表格,但我做出了妥协:左调整所有列标签而不是垂直重叠。

<强>代码

def print_table(h, column_labels_and_spacing, table_label)
  column_labels = column_labels_and_spacing.map(&:first)
  column_spacing = column_labels_and_spacing[0..-2].map(&:last) << 0
  rows = h.map { |k,f|
            [table_label[k]].product(f.map { |field, g|
              [[field, g.values.map(&:to_s)].flatten] }) }
          .flat_map { |e| e.map(&:flatten) }.unshift(column_labels)
  column_widths = rows.transpose.map { |c| c.max_by(&:size).size }
                      .zip(column_spacing).map { |a,b| a+b }
  rows.each do |row|
    row.zip(column_widths).each { |s, width| print s.ljust(width) }
    puts
  end  
end

示例

输入

h = {"Table1"=>
      {"Acct type"=>{"Expected"=>"ACC",   "Actual"=>"ACC"},
       "Seq No"   =>{"Expected"=>"100.0", "Actual"=>100},
       "Class"    =>{"Expected"=>"AC",    "Actual"=>"AC"}},
     "Table 2"=>
       {"Date"    =>{"Expected"=>"20140606",  "Actual"=>"20130606"}}}

column_labels_and_spacing = [["Table", 1], ["Field", 2],
                             ["Expected", 3], ["Actual"]]
table_label = { "Table1"=>"Table 1", "Table 2"=>"Table 2" }

为了简化说明,我将BigDecimal值转换为Fixnum

调用方法

print_table(h, column_labels_and_spacing, table_label)
  #->  Table   Field      Expected   Actual  
  #    Table 1 Acct type  ACC        ACC     
  #    Table 1 Seq No     100.0      100     
  #    Table 1 Class      AC         AC      
  #    Table 2 Date       20140606   20130606

<强>解释

对于上面的例子:

column_labels = column_labels_and_spacing.map(&:first)
  #=> ["Table", "Field", "Expected", "Actual"] 
column_spacing = column_labels_and_spacing[0..-2].map(&:last) << 0
  #=> [1, 2, 3, 0] 

a = h.map { |k,f|
       [table_label[k]].product(f.map { |field, g|
         [[field, g.values.map(&:to_s)].flatten] }) }
  # => [[["Table 1", [["Acct type", "ACC", "ACC"]]],
  #      ["Table 1", [["Seq No", "100.0", "100"]]],
  #      ["Table 1", [["Class", "AC", "AC"]]]],
  #    [["Table 2", [["Date", "20140606", "20130606"]]]]] 

要了解如何计算a,请考虑传递给块 1 的第一个键值对h,该键被赋予块变量{{ 1}}和分配给k的值:

f

然后该块执行以下计算:

  k = "Table1"
  f = { "Acct type"=>{ "Expected"=>"ACC", "Actual"=>"ACC" } }

现在让我们继续,计算 b = f.map { |field, g| [[field, g.values.map(&:to_s)].flatten] } # => [["Acct type", "ACC", "ACC"]] c = ["Table 1"].product([[["Acct type", "ACC", "ACC"]]]) #=> [["Table 1", [["Acct type", "ACC", "ACC"]]]] d = c.flatten #=> ["Table 1", "Acct type", "ACC", "ACC"] 。计算表格主体的行:

a

然后添加列标签:

  e = a.flat_map { |e| e.map(&:flatten) }
    #=> [["Table 1", "Acct type", "ACC", "ACC"],
    #    ["Table 1", "Seq No", "100.0", "100"],
    #    ["Table 1", "Class", "AC", "AC"],
    #    ["Table 2", "Date", "20140606", "20130606"]] 

接下来,计算列宽:

  rows = e.unshift(column_labels)
    #=> [["Table", "Field", "Expected", "Actual"],
    #    ["Table 1", "Acct type", "ACC", "ACC"],
    #    ["Table 1", "Seq No", "100.0", "100"],
    #    ["Table 1", "Class", "AC", "AC"],
    #    ["Table 2", "Date", "20140606", "20130606"]] 

考虑打印表格的第二行(列标签后的第一行):

f = rows.transpose
  #=> [["Table", "Table 1", "Table 1", "Table 1", "Table 2"],
  #    ["Field", "Acct type", "Seq No", "Class", "Date"],
  #    ["Expected", "ACC", "100.0", "AC", "20140606"],
  #    ["Actual", "ACC", "100", "AC", "20130606"]] 

g = f.map { |c| c.max_by(&:size).size }
  #=> [7, 9, 8, 8] 

i = g.zip(column_spacing)
  # => [[7, 1], [9, 2], [8, 3], [8, 0]]

column_widths = i.map { |a,b| a+b }
  #=> [8, 11, 11, 8]

rows.each do |row|
  row.zip(column_widths).each { |s, width| print s.ljust(width) }
  puts
end  

打印第一个字段:

row = ["Table 1", "Acct type", "ACC", "ACC"]
row.zip(column_widths)
  # => [["Table 1", 8], ["Acct type", 11], ["ACC", 11], ["ACC", 8]]

我打印冒号只是为了显示字段宽度。其他字段的印刷方式类似。

1 Ruby 1.9+需要知道哪个键值对首先传递给块。