如何实例化具有任意深度的多级哈希

时间:2015-11-17 16:53:46

标签: ruby

我想根据数据库查询的结果创建一个哈希,使其键是除最后一个列之外的列值,并且该值是最后一列值(或默认值)。例如,如果我有行:

1 2 3 1
1 2 4 9
1 3 2 nil

和默认值111,我应该得到:

{
  1 => 
       {
          2 => { 3 => 1, 4 => 9},
          3 => { 2 => 111}
       }
}

我想让方法足够通用以处理任意数量的列,因此签名可以是:

to_lookup(rows, default_value, value_column, *columns)

我该怎么做?

更新:忘记了输出中的逗号。

2 个答案:

答案 0 :(得分:3)

[编辑:看完@ cthulhu&#39的回答后,我想我可能误解了这个问题。我假设将连续的行分组,而不是所有要分组的行。我将为前一种解释留下我的答案。]

我相信这就是你要找的东西:

def hashify(arr)
  return arr.first.first if arr.first.size == 1  
  arr.slice_when { |f,s| f.first != s.first }.
      each_with_object({}) do |a,h|
        key, *rest = a.transpose
        h[key.first] = hashify(rest.transpose)
      end
end

hashify [[1, 2, 3, 1], [1, 2, 4, 9], [1, 3, 2, nil]]
  #=> {1=>{2=>{3=>1, 4=>9}, 3=>{2=>nil}}} 

hashify [[1, 2, 3, 1], [1, 2, 4, 9], [2, 3, 2, nil]]
  #=> {1=>{2=>{3=>1, 4=>9}}, 2=>{3=>{2=>nil}}} 

使用默认值替换nil可以在构造哈希之前或之后完成。

Enumerable#slice_when在v2.2中被赋予了我们。对于早期版本,您可以替换:

arr.slice_when { |f,s| f.first != s.first }

arr.chunk { |row| row.first }.map(&:last)

答案 1 :(得分:2)

我通过删除传递默认值的能力简化了事情, 我还简化了签名方法只有一个参数。

RSpec.describe "#to_lookup" do

  def to_lookup(rows)
    return rows.first.first if rows.flatten.size == 1
    h = {}
    rows.group_by { |e| e.first }.each_entry do |k, v|
      v.each &:shift
     h[k] = to_lookup(v)
    end
    h
  end


  let :input do
    [
      [1, 2, 3, 1],
      [1, 2, 4, 9],
      [1, 3, 2, 111],
    ]
  end

  let :output do
    {
      1 => {
          2 => {3 => 1, 4 => 9},
          3 => {2 => 111}
      }
    }
  end

  it { expect(to_lookup(input)).to eq(output) }

end

顺便说一下我想知道你想要输出什么输出:

 1 2 3 1
 1 2 3 2

编辑:工作代码段:http://rubysandbox.com/#/snippet/566aefa80195f1000c000000