简体   繁体   English

如何跳过Ruby哈希中的空值

[英]How to skip over null values in Ruby hashes

I have an array in which each array item is a hash with date values, as shown in my example below. 我有一个数组,其中每个数组项都是带有日期值的哈希,如下面的示例所示。 In actuality, it is longer and there are about 20 dates per item instead of 3. What I need to do is get the date interval values for each item (that is, how many days between each date value), and their intervals' medians. 实际上,它比较长,每个项目有大约20个日期而不是3个日期。我需要做的是获取每个项目的日期间隔值(即每个日期值之间有多少天)及其间隔的中位数。 My code is as follows: 我的代码如下:

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

Which returns these date interval values and this median: 返回以下日期间隔值和中间值:

4
0
5
Median: 4

However, some of these items' values are empty, as in the first item, in its :date_three value. 但是,与第一项一样,这些项的某些值的:date_three值为空。 If I try to run the same equations for the :date_three to :date_two values , as follows, it will throw an error because the last :date_three value is empty. 如果我尝试对:date_three:date_two values运行相同的方程式,如下所示,则将抛出错误,因为最后一个:date_three值为空。

It's okay that I can't get that interval, but I would still would need the next two items date intervals (which would be 10 and 1). 没关系,我可以得到这个间隔,但是我仍然需要接下来的两个日期间隔(分别是10和1)。

How can I skip over intervals that return errors when I try to run them? 尝试运行错误时,如何跳过返回错误的时间间隔?

I would recommend adding helper functions that can deal with the types of inputs you're expecting. 我建议添加可以处理您期望的输入类型的辅助函数。 For instance: 例如:

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

The bonus here is that you can then add unit tests for the individual components so that you can easily test edge cases (nil dates, empty string dates, etc). 这样做的好处是,您可以随后为各个组件添加单元测试,以便轻松测试边缘情况(零日期,空字符串日期等)。

In your map block, you can just add a check to make sure the values aren't blank 在地图块中,您只需添加一个检查以确保值不为空

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

I would first just filter out the empty values first (I check if the string consists entirely of whitespace or is empty), then compare the remaining values using your existing code. 首先,我将首先过滤掉空值(我检查字符串是否完全由空格组成或为空),然后使用现有代码比较其余值。 I added a loop which will compare all values in the sequence to the next value. 我添加了一个循环,该循环将序列中的所有值与下一个值进行比较。

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]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM