Ruby on rails 我如何加快Ruby/Rake任务的速度,该任务统计30万个日期字符串中出现的日期?

Ruby on rails 我如何加快Ruby/Rake任务的速度,该任务统计30万个日期字符串中出现的日期?,ruby-on-rails,ruby,performance,optimization,rake,Ruby On Rails,Ruby,Performance,Optimization,Rake,我有一个300K字符串数组,表示日期: date_array = [ "2007-03-25 14:24:29", "2007-03-25 14:27:00", ... ] 我需要计算此数组中每个日期的出现次数(例如,“2011-03-25”的所有日期字符串)。确切的时间并不重要,只是日期。我知道文件中的日期范围。因此,我: Date.parse('2007-03-23').upto Date.parse('2011-10-06') do |date_to_count| cou

我有一个300K字符串数组,表示日期:

date_array = [
  "2007-03-25 14:24:29",
  "2007-03-25 14:27:00",
  ...
]
我需要计算此数组中每个日期的出现次数(例如,“2011-03-25”的所有日期字符串)。确切的时间并不重要,只是日期。我知道文件中的日期范围。因此,我:

Date.parse('2007-03-23').upto Date.parse('2011-10-06') do |date_to_count|
  count = 0
  date_array.each do |date_string|
    if Date.parse(date_string) >= date_to_count && 
       Date.parse(date_string) <= date_to_count
      count += 1
    end
  end
  puts "#{date_to_count} occurred #{count} times."
end
Date.parse('2007-03-23')。update.parse('2011-10-06')do | Date|to|u count|
计数=0
日期数组。每个do |日期字符串|
如果Date.parse(Date\u string)>=Date\u to\u count&&

parse(Date\u string)这是一个非常糟糕的算法。您正在扫描每个日期的整个列表,而且,您在没有明显原因的情况下两次解析同一日期。这意味着对范围内的N个日期和列表中的M个日期进行N*M*2次日期解析

您真正需要的是使用
group\u by
并一次性完成:

dates = date_array.group_by do |date_string|
  Date.parse(date_string)
end
然后您可以将其用作计数的参考:

Date.parse('2007-03-23').upto Date.parse('2011-10-06') do |date_to_count|
  puts "#{date_to_count} occurred #{dates[date_to_count] ? dates[date_to_count].length : 0} times."
end

是的,如果日期的格式相同,则根本不需要解析日期。了解您的数据是您可以拥有的最强大的工具之一

如果datetime字符串的格式都相同(yyyy-mm-dd HH:mm:SS),则可以执行以下操作

data_array.group_by{|datetime| datetime[0..9]}
这将为您提供一个类似的哈希,其中日期字符串作为键,日期数组作为值

{
  "2007-05-06" => [...],
  "2007-05-07" => [...],
  ...
}
{
  "2007-05-06" => 123,
  "2007-05-07" => 456,
  ...
}
所以你必须得到每个数组的长度

data_array.group_by{|datetime| datatime[0..9]}.each do |date_string, date_array|
  puts "#{date_string} occurred #{date_array.length} times."
end
当然,当您不需要日期数组时,这种方法是在浪费内存

那你呢

一种更节省内存的方法

date_counts = {}
date_array.each do |date_string|
  date = date_string[0..9]
  date_counts[date] ||= 0 # initialize count if necessary
  date_counts[date] += 1
end
您将得到一个哈希,日期字符串作为键,计数作为值

{
  "2007-05-06" => [...],
  "2007-05-07" => [...],
  ...
}
{
  "2007-05-06" => 123,
  "2007-05-07" => 456,
  ...
}
将所有内容组合在一起

date_counts = {}
date_array.each do |date_string|
  date = date_string[0..9]
  date_counts[date] ||= 0 # initialize count if necessary
  date_counts[date] += 1
end

Date.parse('2007-03-23').upto Date.parse('2011-10-06') do |date_to_count|
  puts "#{date_to_count} occurred #{date_counts[date_to_count.to_s].to_i} times."
end

我同意。在我的笔记本电脑上解析30万个日期需要45秒。像这样切片需要0.12秒。哇。非常感谢你。0.48秒。这些计数器哈希的另一个技巧是:
date_counts=Hash.new{h,k{h[k]=0}
,它总是自动将任何键预初始化为零。如果你有创意的话,
Hash.new
的块可以做各种神奇的事情。这将确保始终定义请求的任何密钥,因此无需检查
nil
。感谢您的回答。它教会了我一些有用的东西。我同意向导的答案,因为在我的具体情况下,避免解析日期首先会使通用解决方案更快。完全不解析肯定是更好的解决方案。