如何将Array转换为Hash:整数计数

时间:2013-04-29 01:54:32

标签: ruby

我正在进行迭代:this的时间定位部分(来自Jumpstart Lab的活动管理器练习)。我想出了如何迭代数组并将其转换为this线程的哈希值。这就是我所拥有的:

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

3 个答案:

答案 0 :(得分: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}) }

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

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也适用于Hashes,但它返回的是一个数组而不是另一个Hash,所以你必须在完成后将其转换回来。这样就可以了:

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

答案 1 :(得分: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}

答案 2 :(得分:1)

为作业选择正确的数据结构非常重要。在这种情况下,正确的数据结构不是Hash,而是Multiset。 [不幸的是,标准库或核心库中没有Multiset,但有一个multiset gem。]

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>

如您所见,第十个小时有一个条目,第13个小时和第14个小时分别有两个条目。