Finding Matches in Array of Classes


#1

I have an ActiveRecord find result. An element’s contents look like:

irb(main):090:0> results[0]
=> #<InventoryHour id: 47475, observation_time: “2008-01-01 00:00:00”,
station_id: 6, extension_id: 22, disk: 4, archive: 0, updated_on:
“2009-04-15 15:57:16”>

The class of “observation_time” is:
results[0].observation_time.class
=> Time

What I’d like to do is find those elements which match on a particular
range of “observation_time”. I’m used to Matlab and IDL programming
where one just does something like this:

indices = where( results.observation_time >= “2008-1-1” and
results.observation_time < “2009-1-1” )

What’s the ruby way? Obviously, I can’t use Array.include? but
something like that would be nice.

Rob


#2

On Wed, Apr 29, 2009 at 2:03 PM, Rob R. removed_email_address@domain.invalid
wrote:

=> Time
something like that would be nice.
Depends. This might work (just an example)…

d = Time.now
e = Time.now + 1
f = Time.now + 2
(d…f).include? e
=> true

Todd


#3

Rob R. wrote:

Rob

require ‘time’
range = Time.parse(“2008-1-1”)…Time.parse(“2009-1-1”)
results.select {|r| range.include? r.observation_time}


#4

On Wed, Apr 29, 2009 at 2:21 PM, Todd B. removed_email_address@domain.invalid
wrote:

results[0].observation_time.class
What’s the ruby way? Obviously, I can’t use Array.include? but
something like that would be nice.

Depends. This might work (just an example)…

d = Time.now
e = Time.now + 1
f = Time.now + 2
(d…f).include? e
=> true

beware, though, since (d…e).include?(e) is false, but
(d…e).include?(d) is true.

Todd


#5

Rob R. wrote:

What’s the ruby way?

now = Time.now
in_a_minute = now + 60
in_two_minutes = now + 2*60

if in_a_minute.between?(now, in_two_minutes)
puts ‘yes1’
end

if in_a_minute.between?(in_a_minute, in_two_minutes)
puts ‘yes2’
end

if in_a_minute.between?(now, in_a_minute)
puts ‘yes3’
end

if now.between?(in_a_minute, in_two_minutes)
puts ‘yes4’
end

–output:–
yes1
yes2
yes3


#6

7stud – wrote:

if in_a_minute.between?(now, in_two_minutes)
puts ‘yes1’
end

I just thought I’d mention that Time mixes in Comparable, which is where
it gets the between? method from.


#7

On Apr 29, 2:24 pm, Todd B. removed_email_address@domain.invalid wrote:

The class of “observation_time” is:
What’s the ruby way? Obviously, I can’t use Array.include? but
beware, though, since (d…e).include?(e) is false, but
(d…e).include?(d) is true.

Todd

shouldnt you do this kind of filtering on the ActiveRecord#find call?


#8

On Thu, Apr 30, 2009 at 2:17 AM, 7stud – removed_email_address@domain.invalid
wrote:

7stud – wrote:

if in_a_minute.between?(now, in_two_minutes)
puts ‘yes1’
end

Nice.

Todd


#9

snex wrote:

On Apr 29, 2:24�pm, Todd B. removed_email_address@domain.invalid wrote:

The class of “observation_time” is:
What’s the ruby way? �Obviously, I can’t use Array.include? but
beware, though, since (d…e).include?(e) is false, but
(d…e).include?(d) is true.

Todd

shouldnt you do this kind of filtering on the ActiveRecord#find call?

First, thank you for the varied and interesting suggestions. I always
learn a lot here!

My motivation is to present an inventory summary for scientific
observations. A sample is attached. From that motivation, I got
thinking about array processing in general and thus my question.

To create this view, I am greedily calling ActiveRecord#find many times
to essentially work with the same (24 * 366) rows. To me one call to
retrieve all of these rows and then pivot and filter them in local
memory makes more sense; it would be especially cool if I could use the
same “find” like calls without regard to whether the rows really came
from an AR connection or an already available resulset. Being used to
scientific analysis programming where we are always dealing with
multidimensional arrays, I wondered, isn’t there a mechanism to operate
on an entire array, filtering on components which are “comparable”?
Consider the following IDL as pseudocode (Matlab would be similar).
This filter which gives data in the time range of “1990-1-1 12:00:00” to
“1990-1-1 12:59:59” would look like:

indices = where( data.observation_time >= julday( 1, 1, 1990, 12, 0,
  1. ) and data.observation_time < julday( 1, 1, 1990, 13, 0, 0 ) )

However, it seems from these examples, I must implement this
functionality myself (when it becomes necessary). It seems to me I’ll
need to write a function for each possible expression I might have which
essentially just iterates and performs that expression. Consider a
compound filter like (again in IDL):

indices = where( data.quality eq "good" and data.observation_time >=

julday( 1, 1, 1990, 12, 0, 0) ) and data.observation_time < julday( 1,
1, 1990, 13, 0, 0 ) )

This certainly maps very well to an ActiveRecord#find statement. But
what if I already have my resultset available? Or what if I’m not using
a database at all and I simply have an array of objects? For now, in
this database example, I am using ActiveRecord#find, but this situation
had me wondering about Ruby Array filtering in general. I recall way
back when I was in school and programming C++ for engineering analysis.
I wrote a sparse matrix library which overloaded “<” and “&&” to yield
the functionality above. Aren’t usual comparison tokens already
overloaded somewhere to operate on arrays?

Thanks again for the great suggestions!!


#10

Joel’s select/include? implementation is the normal way to solve this
particular problem, but if you’re looking for a more general approach
to dealing with arrays and other ruby data structures, you might look
at Reg, my declarative language for matching ruby data structures.
With Reg, I would use this variation on Joel’s code:

require ‘rubygems’
require ‘reg’
require ‘time’

range = Time.parse(“2008-1-1”)…Time.parse(“2009-1-1”)
#results.select {|r| range.include? r.observation_time}
results.grep -{:observation_time=>range}

For most things Reg can do, it’s syntax is shorter than the equivalent
imperative form, but it’s not so big in simple cases like this.

http://rubyforge.org/projects/reg/
http://github.com/coatl/reg/