0

I have an array of arrays, I want to select arrays with a date that falls in a certain range.

ar = [[72162, "2014-01-21"], 
[53172, "2014-01-22"], 
[49374, "2014-01-23"], 
[41778, "2014-01-24"], 
[34182, "2014-01-25"], 
[58869, "2014-01-26"], 
[72162, "2014-01-27"], 
[43677, "2014-01-28"], 
[37980, "2014-01-29"], 
[87354, "2014-01-30"], 
[43677, "2014-01-31"]]

For example, I'd like to get all arrays between 2014-01-24 and 2014-01-29.

1
  • If any of the answers were of use, consider selecting the one that most helpful to you. Commented Jul 4, 2014 at 1:01

3 Answers 3

4

Without using Date:

ar.select { |_,d| d >= "2014-01-24" && d <= "2014-01-29" }

=> [[41778, "2014-01-24"],
    [34182, "2014-01-25"],
    [58869, "2014-01-26"],
    [72162, "2014-01-27"],
    [43677, "2014-01-28"],
    [37980, "2014-01-29"]]

or

ar.select { |_,d| ("2014-01-24".."2014-01-29").cover?(d) }

Note this depends on the date being expressed in year-month-day order.

Edit: I formerly used what I thought was Range#include?, but @toro2k pointed out that is was actually Enumerable#include?, which is quite slow. I had thought that Range#include? would be able to just compare endpoints, since <=> is defined for Strings. Not so; it only applies when the values are numeric or single character strings (else it supers to Enumerable#include?). That puzzled me. For anyone interested, I think I now understand the reason for the restricted application.

We would want ('aa'..'zz').include?('mno') to behave the same as

('aa'..'zz').to_a.include?('mno') => false

Suppose we do this:

class String
  alias :spaceship :<=>
  def <=>(other)
    spaceship(other.size > 1 ? other[0,2] : other)
  end
end

Then

"mno" >= 'aa' # => true
"mno" <= 'zz' # => true

so if Range#include? only considered the endpoints,

('aa'..'zz').include?("mno") # => true
Sign up to request clarification or add additional context in comments.

11 Comments

One of the nice things about ISO 8601 dates is that they compare sensibly as strings.
@muistooshort this is new to me thanks for the tip, any link to this??
@bjhaid, I think μ just means that since ISO 8601 dates are yyyy-mm-dd, two dates, expressed as strings, can be ordered with <=>; that is, yyyy's are compared, if both are the same, mm's are compared, if the same, dd's are compared.
@bjhaid: Cary's right on that, it even extends to timestamps as all the components are listed in a sensible order &mdash; largest to smallest &mdash; all the way down to the seconds. You still have to normalize the time zones of course.
@CarySwoveland Range#include? just compares string endpoints only if they are one character long, please check the implementation.
|
2
require 'date'
range = Date.parse("2014-01-24")..Date.parse("2014-01-29")
ar.select { |x| range.include?(Date.parse(x[1])) }
=> [[41778, "2014-01-24"],
 [34182, "2014-01-25"],
 [58869, "2014-01-26"],
 [72162, "2014-01-27"],
 [43677, "2014-01-28"],
 [37980, "2014-01-29"]]

Comments

2

I'd use Comparable#between?

ar = [ [72162, "2014-01-21"], 
       [53172, "2014-01-22"], 
       [49374, "2014-01-23"], 
       [41778, "2014-01-24"], 
       [34182, "2014-01-25"], 
       [58869, "2014-01-26"], 
       [72162, "2014-01-27"], 
       [43677, "2014-01-28"], 
       [37980, "2014-01-29"], 
       [87354, "2014-01-30"], 
       [43677, "2014-01-31"]
     ]

ar.select { |_,e| e.between?("2014-01-24","2014-01-29") }
# => [[41778, "2014-01-24"],
#     [34182, "2014-01-25"],
#     [58869, "2014-01-26"],
#     [72162, "2014-01-27"],
#     [43677, "2014-01-28"],
#     [37980, "2014-01-29"]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.