我有一个类似于下面例子的多维数组,我想用Ruby的zip方法将它们组合在一起。当每个内部数组具有相同数量的元素时,我可以正常工作,但是当它们的长度不同时会遇到问题。
在下面的示例中,第二组在00:15缺少记录。 我如何填写此遗失记录?
我在考虑差距是什么?
这是构成一个时间戳的时间戳 间隙。看看我的第一个代码 我有评论的样本 差距在00:15。所有其他的 数组有一个散列 时间戳,所以我认为这是一个 “缺失记录”或“缺口”。该 时间戳确实可能是其他一些 独特的字符串所以他们的事实 相隔15分钟是无关紧要的。 价值观也无关紧要。
想到的唯一方法是将数组循环两次。第一次是构建uniq时间戳的数组,第二次是填写时间戳不存在的缺失记录。我很乐意编写这种方法,但它看起来有点笨拙,Ruby似乎总是以一种优雅而简洁的解决方案让我感到惊讶。
我从这开始:
values = [
[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => 2},
{:timestamp => "2011-01-01 00:30", :value => 3}
],
[ # There's a gap here at 00:15
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:30", :value => 3}
],
[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => 2},
{:timestamp => "2011-01-01 00:30", :value => 3}
]
]
我想以此结束:
values = [
[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => 2},
{:timestamp => "2011-01-01 00:30", :value => 3}
],
[ # The gap has been filled with a nil value
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => nil},
{:timestamp => "2011-01-01 00:30", :value => 3}
],
[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => 2},
{:timestamp => "2011-01-01 00:30", :value => 3}
]
]
当所有数组的大小相同时,values.transpose
将产生:
[
[
{:value=>1, :timestamp=>"2011-01-01 00:00"},
{:value=>1, :timestamp=>"2011-01-01 00:00"},
{:value=>1, :timestamp=>"2011-01-01 00:00"}
],
[
{:value=>2, :timestamp=>"2011-01-01 00:15"},
{:value=>nil, :timestamp=>"2011-01-01 00:15"},
{:value=>2, :timestamp=>"2011-01-01 00:15"}
],
[
{:value=>3, :timestamp=>"2011-01-01 00:30"},
{:value=>3, :timestamp=>"2011-01-01 00:30"},
{:value=>3, :timestamp=>"2011-01-01 00:30"}
]
]
答案 0 :(得分:1)
你概述的方法是正确的,但事实证明ruby非常适合优雅地采用这种方法。这样做可以做到,例如:
stamps = values.map{ |logs| logs.map{ |row| row[:timestamp] } }.flatten.uniq.sort
values.map!{ |logs| stamps.map { |ts| logs.select{ |row| row[:timestamp] == ts }.first || { :timestamp => ts, :value => nil } } }
第一行获取一个唯一时间戳列表(将所有日志映射到时间戳数组中,将数组展平为单个数组,仅保留唯一时间,并对时间戳进行排序)。
第二行填充空白(循环遍历日志,并且对于该日志中的每个时间戳使用那里有什么东西,否则插入新的零值行。)
答案 1 :(得分:1)
这是一个有效的解决方案;它找到所有时间戳,找到每个集合中缺少的时间戳,然后注入它们。在使用Ruby 1.9.2进行小改进的解决方案之后,请参阅注释:
values = [[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => 2},
{:timestamp => "2011-01-01 00:30", :value => 3}
],[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:30", :value => 3}
],[
{:timestamp => "2011-01-01 00:00", :value => 1},
{:timestamp => "2011-01-01 00:15", :value => 2},
{:timestamp => "2011-01-01 00:30", :value => 3}
]]
all_stamps = values.flatten.map{|x| x[:timestamp]}.uniq.sort
values.each do |set|
my_stamps = set.map{ |x| x[:timestamp] }.uniq
missing = all_stamps - my_stamps
set.concat( missing.map{ |stamp| {timestamp:stamp, value:nil} } )
set.replace( set.sort_by{ |x| x[:timestamp] } )
end
require 'pp'
pp values
#=> [[{:timestamp=>"2011-01-01 00:00", :value=>1},
#=> {:timestamp=>"2011-01-01 00:15", :value=>2},
#=> {:timestamp=>"2011-01-01 00:30", :value=>3}],
#=> [{:timestamp=>"2011-01-01 00:00", :value=>1},
#=> {:timestamp=>"2011-01-01 00:15", :value=>nil},
#=> {:timestamp=>"2011-01-01 00:30", :value=>3}],
#=> [{:timestamp=>"2011-01-01 00:00", :value=>1},
#=> {:timestamp=>"2011-01-01 00:15", :value=>2},
#=> {:timestamp=>"2011-01-01 00:30", :value=>3}]]
使用Ruby 1.9.2,您可以使用set.replace( set.sort_by{...} )
替换set.sort_by!{ ... }
。另请注意,我假设您在我的哈希文字中使用了Ruby 1.9(见missing.map...
)。
答案 2 :(得分:0)
如果您使用的是Rails,请结帐Array#in_groups_of
%w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
["1", "2", "3"]
["4", "5", "6"]
["7", nil, nil]
http://weblog.rubyonrails.org/2006/3/1/new-in-rails-enumerable-group_by-and-array-in_groups_of