1

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

我有

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

我想在 :date 之前折叠这些,最终得到

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

如果列在每行中的顺序相同,则数组数组在最后一步也可以。另外,重要的是,我事先并不知道所有的哈希键(也就是说,我不知道 :width、:height 或 :depth ——它们可能是 :cats、:dogs 和 :hamsters)。

4

4 回答 4

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
于 2009-06-13T19:10:40.193 回答
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)}}
于 2009-06-13T15:18:00.083 回答
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 构造函数,它允许您提供一个在访问不存在的键时调用的块。所以我让哈希创建了一个空哈希,以便我们使用我们找到的值进行更新。然后我只是使用日期作为散列键,按日期对散列值进行排序,我们就完成了转换。

于 2009-06-13T15:38:08.990 回答
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-members 的信息,请参阅此页面

当排序很重要时,您必须使用锯齿状数组:

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
于 2009-06-13T15:07:51.463 回答