如何在Ruby哈希中跳过空值

时间:2022-04-30 14:30:41

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. My code is as follows:

我有一个数组,其中每个数组项都是一个带日期值的哈希,如下面的例子所示。实际上,它更长,每个项目大约有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

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. 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_three运行相同的等式:date_two值,如下所示,它将抛出一个错误,因为last: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?

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

3 个解决方案

#1


1  

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).

这里的好处是,您可以为各个组件添加单元测试,以便您可以轻松测试边缘情况(零日期,空字符串日期等)。

#2


0  

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

#3


0  

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

#1


1  

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).

这里的好处是,您可以为各个组件添加单元测试,以便您可以轻松测试边缘情况(零日期,空字符串日期等)。

#2


0  

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

#3


0  

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