惯用Ruby:数据结构转换

时间:2009-06-13 14:49:19

标签: ruby data-structures hash merge transformation

执行以下数据结构转换的“Rubyist”方式是什么:

我有

    incoming = [ {:date => 20090501, :width => 2}, 
                 {:date => 20090501, :height => 7}, 
                 {:date => 20090501, :depth => 3}, 
                 {:date => 20090502, :width => 4}, 
                 {:date => 20090502, :height => 6}, 
                 {:date => 20090502, :depth => 2},
               ]

我希望通过以下方式折叠这些:日期,以

结束
    outgoing = [ {:date => 20090501, :width => 2, :height => 7, :depth => 3},
                 {:date => 20090502, :width => 4, :height => 6, :depth => 2},
               ]

如果列的每一行中的列顺序相同,那么最后一步也可以使用数组数组。另外,重要的是,我事先并不知道所有的哈希键(也就是说,我不知道:宽度,高度,或:深度 - 它们可能是:猫,狗,和:仓鼠)。

4 个答案:

答案 0 :(得分:8)

如果使用Ruby 1.8.7或Ruby 1.9+,以下代码可以很好地阅读:

incoming.group_by{|hash| hash[:date]}.map do |_, hashes| 
  hashes.reduce(:merge)
end

块属性(_,哈希)中的下划线表示我们不需要/关心该特定属性。

#reduce是#inject的别名,用于集合缩减为单个项目。在新的Ruby版本中,它还接受一个符号,这是用于执行缩减的方法的名称。

首先调用集合中第一个项目的方法,然后将第二个项目作为参数。然后,它会在结果上再次调用该方法,并将第三个项目作为参数,依此类推,直到没有其他项目为止。

[1, 3, 2, 2].reduce(:+) => [4, 2, 2] => [6, 2] => 8

答案 1 :(得分:2)

这是一个班轮:)

incoming.inject({}){ |o,i| o[i[:date]]||=[];o[i[:date]]<<i;o}.map{|a| a[1].inject(){|o,i| o.merge(i)}}

但实际上前一篇文章更清晰,也可能更快。

编辑:进行一些优化:

p incoming.inject(Hash.new{|h,k| h[k]=[]}){ |o,i| o[i[:date]]<<i;o}.map{|a| a[1].inject(){|o,i| o.merge(i)}}

答案 2 :(得分:2)

简明的解决方案:

incoming = [ {:date => 20090501, :width => 2}, 
             {:date => 20090501, :height => 7}, 
             {:date => 20090501, :depth => 3}, 
             {:date => 20090502, :width => 4}, 
             {:date => 20090502, :height => 6}, 
             {:date => 20090502, :depth => 2},
           ]

temp = Hash.new {|hash,key| hash[key] = {}}
incoming.each {|row| temp[row[:date]].update(row)}
outgoing = temp.values.sort {|*rows| rows[0][:date] <=> rows[1][:date]}

这里唯一棘手的是Hash构造函数,它允许您提供在访问不存在的键时调用的块。所以我让Hash创建一个空哈希,让我们用我们找到的值进行更新。然后我只使用日期作为哈希键,按日期对哈希值进行排序,然后我们进行了转换。

答案 3 :(得分:0)

试试这个:

incoming = [ {:date => 20090501, :width => 2}, 
                 {:date => 20090501, :height => 7}, 
                 {:date => 20090501, :depth => 3}, 
                 {:date => 20090502, :width => 4}, 
                 {:date => 20090502, :height => 6}, 
                 {:date => 20090502, :depth => 2},
               ]

# Grouping by `:date`
temp = {}

incoming.each do |row|
    if temp[row[:date]].nil? 
        temp[row[:date]] = []
    end

    temp[row[:date]] << row
end      

# Merging it together
outcoming = []         

temp.each_pair do |date, hashlist|
    res = {}
    hashlist.each do |hash|
        res.merge!(hash)
    end
    outcoming << res 
end

有关hash成员的信息,请参阅this page

订购很重要时,必须使用锯齿状数组:

incoming = [ {:date => 20090501, :width => 2}, 
                 {:date => 20090501, :height => 7}, 
                 {:date => 20090501, :depth => 3}, 
                 {:date => 20090502, :width => 4}, 
                 {:date => 20090502, :height => 6}, 
                 {:date => 20090502, :depth => 2},
               ]

# Grouping by `:date`
temp = {}

incoming.each do |row|
    if temp[row[:date]].nil? 
        temp[row[:date]] = []
    end
    key = row[:date]
    row.delete :date
    temp[key] << row
end      

# Merging it together
outcoming = []         

temp.each_pair do |date, hashlist|
    res = [:date, date]
    hashlist.each do |hash|
        hash.each_pair {|key, value| res << [key, value] }
    end
    outcoming << res
end