【问题标题】:how to convert Array to Hash: integer count如何将数组转换为哈希:整数计数
【发布时间】:2013-04-22 14:50:49
【问题描述】:

我正在处理迭代: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放在哪里?

【问题讨论】:

  • 好吧,你不应该多次使用time作为变量名开始。
  • 你能更好地解释一下你想用哈希做什么吗?

标签: ruby


【解决方案1】:

这个逻辑不正确:

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

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

【讨论】:

【解决方案2】:

你可以用一行得到结果:

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}

【讨论】:

    【解决方案3】:

    为工作选择正确的数据结构很重要。在这种情况下,正确的数据结构不是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>
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-29
      • 2019-10-27
      • 2019-05-23
      • 2015-11-26
      • 1970-01-01
      • 1970-01-01
      • 2010-12-11
      相关资源
      最近更新 更多