如何在Ruby哈希中跳过空值

时间:2014-02-24 20:40:41

标签: ruby-on-rails ruby arrays hash

我有一个数组,其中每个数组项都是一个带日期值的哈希,如下面的例子所示。实际上,它更长,每个项目大约有20个日期而不是3.我需要做的是获取每个项目的日期间隔值(即每个日期值之间的天数)和它们的间隔中位数。我的代码如下:

require 'csv'
require 'date'

dateArray = [{:date_one => "May 1", :date_two =>"May 5", :date_three => " "}, {:date_one => "May 10", :date_two =>"May 10", :date_three => "May 20"}, {:date_one => "May 6", :date_two =>"May 11", :date_three => "May 12"}]

public
def median
sorted = self.sort
  len = sorted.length
  return (sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end

puts dateIntervals = dateArray.map{|h| (DateTime.parse(h[:date_two]) - DateTime.parse(h[:date_one])).to_i}
puts "\nMedian: " 
puts dateIntervals.median

返回这些日期间隔值和此中位数:

4
0
5
Median: 4

但是,其中一些项的值为空,如第一项中的:date_three值。如果我尝试为:date_three:date_two values运行相同的等式,如下所示,它将引发错误,因为最后:date_three值为空。

我可以无法获得该间隔,但我仍然需要接下来的两个项目日期间隔(分别为10和1)。

当我尝试运行它们时,如何跳过返回错误的间隔?

3 个答案:

答案 0 :(得分:1)

我建议添加可以处理您期望的输入类型的辅助函数。例如:

def date_diff(date_one, date_two)
    return nil if date_one.nil? || date_two.nil?
    (date_one - date_two).to_i
end

def str_to_date(input_string)
    DateTime.parse(input_string)
    rescue
    nil
end

dateArray.map{|h| date_diff(str_to_date(h[:date_three]), str_to_date(h[:date_two])) }
=> [nil, 10, 1]

dateArray.map{|h| date_diff(str_to_date(h[:date_three]), str_to_date(h[:date_two])) }.compact.median
=> 5.5

这里的奖励是你可以为各个组件添加单元测试,这样你就可以轻松测试边缘情况(零日期,空字符串日期等)。

答案 1 :(得分:0)

在地图块中,您只需添加一项检查,以确保值不是空白

dateIntervals = dateArray.map{ |h| 
  (DateTime.parse(h[:date_two]) - DateTime.parse(h[:date_one])).to_i unless any_blank?(h)
}

def any_blank?(h)
  h.each do |k, v|
    return true if v == " "
  end
end

答案 2 :(得分:0)

我首先要过滤掉空值(我检查字符串是否完全由空格组成或为空),然后使用现有代码比较剩余的值。我添加了一个循环,它将序列中的所有值与下一个值进行比较。

dateArray = [
  { date_one: "May 1", date_two: "May 5", date_three: " ", date_four: "" },
  { date_one: "May 10", date_two: "May 10", date_three: "May 20" }
]

intervals = dateArray.map do |hash|
  filtered = hash.values.reject { |str| str =~ /^\s*$/ }
  (0...filtered.size-1).map { |idx| (DateTime.parse(filtered[idx+1]) - DateTime.parse(filtered[idx])).to_i }
end

# => [[4], [0, 10]]