1

我正在研究迭代:时间目标部分来自 Jumpstart Lab 的事件管理器练习)。我想出了如何遍历数组并将其转换为来自线程的哈希。这就是我所拥有的:

require "date"

    time = ["11/12/08 10:47","11/12/08 13:23","11/12/08 13:30","11/12/08 14:04","11/12/08 14:46"]

    #makes array of all times
    time.each do |time|
        @array = Array(DateTime.strptime(time,'%m/%d/%Y %H:%M').hour)
    end


    #makes hash of :time => number of instances
     result = Hash.new(0)
      @array.each do |time|
        result[time] += 1  
      end
     puts result

我运行它时得到的哈希是{ 14 => 1 },这不是我想要的。我怀疑发生这种情况是因为我的数组是由整数组成的,而不是像示例中的字符串。有没有办法对整数实现同样的效果?还是我必须将整数转换为字符串?如果我必须转换为字符串,我应该放在to_s哪里?

4

3 回答 3

2

这个逻辑是不正确的:

time.each do |time|
    @array = Array(DateTime.strptime(time,'%m/%d/%Y %H:%M').hour)
end

循环的主体正在替换@array每次的内容,所以当你完成时你只会得到最后一个元素。此外,使用time内部变量名会造成混淆(并且在旧版本的 Ruby 中具有破坏性)。

您想要的是在循环内附加到数组,而不是使用:<<=

@array = []
time.each do |t|
     @array << DateTime.strptime(t, '%m/%d/%Y %H:%M').hour
end

但这是构建数组的一种非常程序化的方式,而不是非常 Rubyish。惯用的方法是使用map一次构造新数组:

@array = time.map { |t| DateTime.strptime(t, '%m/%d/%Y %H:%M').hour }

作为旁注,我不确定您为什么决定创建@array一个实例变量。你可以称之为array;使用@数组是 Perl 的事情,而不是 Ruby。

无论如何,一旦您修复了 的创建@array,您构建计数哈希的逻辑应该按原样工作。但是,您可以使用reduce;以类似的方式构建它。这只是一种可能性:

result = @array.reduce({}) { |h, t| h.merge({t => h[t]+1}) }

您可以通过使用名为 Ruby 数组的内置方法进一步简化逻辑group_by。这个电话:

time.group_by { |t| DateTime.strptime(t, '%m/%d/%Y %H:%M').hour }

返回此哈希:

{10=>["11/12/08 10:47"], 13=>["11/12/08 13:23", "11/12/08 13:30"], 14=>["11/12/08 14:04", "11/12/08 14:46"]}

这接近你想要的result; 您所要做的就是用它们的长度替换这些数组值。幸运的是,map它也适用于 Hash,但它返回的是一个数组数组而不是另一个 Hash,因此您必须在完成后将其转换回来。这可以解决问题:

result = Hash[time.group_by { |t| DateTime.strptime(t, '%m/%d/%Y %H:%M').hour }.map{|k,v| [k, v.length]}]
于 2013-04-29T02:07:06.513 回答
1

你可以用一行得到结果:

result = Hash[time.group_by{|str| DateTime.strptime(str,'%m/%d/%Y %H:%M').hour}.map{|k,v| [k, v.count]}]

解释:

其实分三步:

> step_1 = time.group_by{|str| DateTime.strptime(str,'%m/%d/%Y %H:%M').hour}
=> {10=>["11/12/08 10:47"], 13=>["11/12/08 13:23", "11/12/08 13:30"], 14=>["11/12/08 14:04", "11/12/08 14:46"]}

> step_2 = step_1.map{|k,v| [k, v.count]}
=> [[10, 1], [13, 2], [14, 2]]

> step_3 = Hash[step_2]
=> {10=>1, 13=>2, 14=>2}
于 2013-04-29T02:15:02.137 回答
1

为工作选择正确的数据结构很重要。在这种情况下,正确的数据结构不是 a Hash,而是 a Multiset。【可惜Multiset标准库和核心库里都没有,不过有个multisetgem。】

require 'date'
require 'multiset'

time = [
  '11/12/08 10:47', 
  '11/12/08 13:23', 
  '11/12/08 13:30', 
  '11/12/08 14:04', 
  '11/12/08 14:46'
]

Multiset[*time.map {|t| DateTime.strptime(t, '%m/%d/%Y %H:%M').hour }]
# => #<Multiset:#1 10, #2 13, #2 14>

如您所见,在第 10 个小时有一个条目,在第 13 和第 14 个小时各有两个条目。

于 2013-04-29T13:24:33.100 回答