Range to array that acts like time objects?

Hello everybody,

I’m looking for a way to create an array out of a range, but does the
counting as like it’s a time object. So for instance:

(800…900).to_a (which represents: 08:00 - 09:00)
will generate an array something like this:

#=> [800,801,802,… 899, 900]

Whereas I would like the output to be like this:

#=> [800,801,802,… 859, 900]

Basically it should bump to the next major number (hour) after 59
instead of going all the way up to 99.

How should I go about doing this?

Thanks!

Sorry if my answer was a bit air-headed. Here’s a more tangible
example:

class HoursMinutes
  attr_accessor :hours
  attr_reader :minutes
  def initialize h,m
    @hours = h
    self.minutes = m
  end
  def <=> o
    r = (@hours <=> o.hours)
    r = (@minutes <=> o.minutes) if r == 0
    r
  end
  def succ
    HoursMinutes.new(@hours, @minutes+1)
  end
  def minutes= m
    while m > 60
      @hours += 1
      m -= 60
    end
    while m < 0
      @hours -= 1
      m += 60
    end
    @minutes = m
  end
  def to_s
    "%d:%02d" % [@hours, @minutes]
  end
end

range = HoursMinutes.new(8,0)..HoursMinutes.new(9,0)
p range.to_a

The spaceship operator will break if o doesn’t quack like an
HoursMinutes object, and it’s not very useful, but it might be a
decent starting place.

On 13 September 2012 21:37, Matthew K. [email protected]
wrote:

I’m looking for a way to create an array out of a range, but does the


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd

I’d create a class, maybe class HoursMinutes, which has @hour and
@minute properties, defines a #<=> comparison method and a #succ that
increments @minute with overflow into @hour. Then I’d make the range
HoursMinutes.new(8,0)..HoursMinutes.new(9,0)

But that’s just me.

On 13 September 2012 21:31, Jermaine O. [email protected] wrote:

Whereas I would like the output to be like this:

Posted via http://www.ruby-forum.com/.


Matthew K., B.Sc (CompSci) (Hons)
http://matthew.kerwin.net.au/
ABN: 59-013-727-651

“You’ll never find a programming language that frees
you from the burden of clarifying your ideas.” - xkcd

On Thu, Sep 13, 2012 at 08:31:44PM +0900, Jermaine O. wrote:

instead of going all the way up to 99.
Do you just want to throw away values where the mod(100) values are
greater than 59?

(800..900).reject { |n| (n % 100) > 59 }
# => [800, 801, 802, 803, …, 900]

Or do you want to map base-10 numbers to a kind of untyped base-60
format?

(800..900).map { |n| (n / 60)*100 + (n % 60) }
# => [1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329,
1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340,
1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351,
1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1400, 1401, 1402,
1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413,
1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424,
1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435,
1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446,
1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457,
1458, 1459, 1500]

Thanks Matthew,

Pretty interesting, I’ll definitely give it a try.
You do have one typo, in the minutes method it should be:
while m >= 60 instead of > 60 :slight_smile:

I’m still looking for more ways though, how I could fix this more
elegantly, but this is a good starting point. Thanks again!

Matthew K. wrote in post #1075787:

Sorry if my answer was a bit air-headed. Here’s a more tangible
example:

class HoursMinutes
  attr_accessor :hours
  attr_reader :minutes

Hi Sung,

(800…900).reject { |n| (n % 100) > 59 } is just what I need. Sweet!
Thanks a bunch

Sung P. wrote in post #1075802:

On Thu, Sep 13, 2012 at 08:31:44PM +0900, Jermaine O. wrote:

instead of going all the way up to 99.
Do you just want to throw away values where the mod(100) values are
greater than 59?

(800..900).reject { |n| (n % 100) > 59 }
# => [800, 801, 802, 803, …, 900]

Or do you want to map base-10 numbers to a kind of untyped base-60
format?

(800..900).map { |n| (n / 60)*100 + (n % 60) }
# => [1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329,
1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340,
1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351,
1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1400, 1401, 1402,
1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413,
1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424,
1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435,
1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446,
1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457,
1458, 1459, 1500]

maybe you want to use Time?

arr = []
hour1 = Time.gm(2000,“jan”,1,8,0)
hour2 = Time.gm(2000,“jan”,1,9,0)
while hour1 <= hour2
arr << hour1.hour.to_s + “:” + hour1.min.to_s.rjust(2,“0”)
hour1 += 60
end

p arr

Hi,

What’s the background of this, what do you want to do?

Storing actual sequence values in an array rarely makes sense. It’s
usually better to do this abstractly with iterators where you only
specify the start and the end and then get the values in between “on
demand”.

The class by Matthew already has this feature as it can be used in
ranges.

@Jan E.
Well I’m basically experimenting with a very simple sort of a
availability time checker.

So let’s say user A sets its availability from 08:00 - 12:00
Where I’m storing this in the database as: 800…1200 for that user.

It’s also possible that he is busy at a certain time, say: 10:00 - 1200
I also store this ‘busy time’ as: 1000…1200

Now I can get a list of availability hours like so
user_available = (800…1200).to_a
user_busy = (1000…1200).to_a

user_still_available = user_available - user_busy
#=> [800, 801,802…859, 900]

Other suggestions, better solutions or ideas to go about this are more
then welcome!

Jan E. wrote in post #1075806:

Hi,

What’s the background of this, what do you want to do?

Storing actual sequence values in an array rarely makes sense. It’s
usually better to do this abstractly with iterators where you only
specify the start and the end and then get the values in between “on
demand”.

The class by Matthew already has this feature as it can be used in
ranges.

On Thu, Sep 13, 2012 at 2:42 PM, Sung P. [email protected] wrote:

format?
1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457,
1458, 1459, 1500]

We can use Fixnum#divmod here

irb(main):004:0> 61.times.map {|x| a,b=x.divmod 60; 800+a*100+b}
=> [800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812,
813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826,
827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840,
841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854,
855, 856, 857, 858, 859, 900]

For times I’d probably rather use Time though:

irb(main):012:0> start = Time.local 2012,9,13,8
=> 2012-09-13 08:00:00 +0200
irb(main):013:0> 61.times.map {|m| start + m * 60}
=> [2012-09-13 08:00:00 +0200, 2012-09-13 08:01:00 +0200, 2012-09-13
08:02:00 +0200, 2012-09-13 08:03:00 +0200, 2012-09-13 08:04:00 +0200,
2012-09-13 08:05:00 +0200, 2012-09-13 08:06:00 +0200, 2012-09-13
08:07:00 +0200, 2012-09-13 08:08:00 +0200, 2012-09-13 08:09:00 +0200,
2012-09-13 08:10:00 +0200, 2012-09-13 08:11:00 +0200, 2012-09-13
08:12:00 +0200, 2012-09-13 08:13:00 +0200, 2012-09-13 08:14:00 +0200,
2012-09-13 08:15:00 +0200, 2012-09-13 08:16:00 +0200, 2012-09-13
08:17:00 +0200, 2012-09-13 08:18:00 +0200, 2012-09-13 08:19:00 +0200,
2012-09-13 08:20:00 +0200, 2012-09-13 08:21:00 +0200, 2012-09-13
08:22:00 +0200, 2012-09-13 08:23:00 +0200, 2012-09-13 08:24:00 +0200,
2012-09-13 08:25:00 +0200, 2012-09-13 08:26:00 +0200, 2012-09-13
08:27:00 +0200, 2012-09-13 08:28:00 +0200, 2012-09-13 08:29:00 +0200,
2012-09-13 08:30:00 +0200, 2012-09-13 08:31:00 +0200, 2012-09-13
08:32:00 +0200, 2012-09-13 08:33:00 +0200, 2012-09-13 08:34:00 +0200,
2012-09-13 08:35:00 +0200, 2012-09-13 08:36:00 +0200, 2012-09-13
08:37:00 +0200, 2012-09-13 08:38:00 +0200, 2012-09-13 08:39:00 +0200,
2012-09-13 08:40:00 +0200, 2012-09-13 08:41:00 +0200, 2012-09-13
08:42:00 +0200, 2012-09-13 08:43:00 +0200, 2012-09-13 08:44:00 +0200,
2012-09-13 08:45:00 +0200, 2012-09-13 08:46:00 +0200, 2012-09-13
08:47:00 +0200, 2012-09-13 08:48:00 +0200, 2012-09-13 08:49:00 +0200,
2012-09-13 08:50:00 +0200, 2012-09-13 08:51:00 +0200, 2012-09-13
08:52:00 +0200, 2012-09-13 08:53:00 +0200, 2012-09-13 08:54:00 +0200,
2012-09-13 08:55:00 +0200, 2012-09-13 08:56:00 +0200, 2012-09-13
08:57:00 +0200, 2012-09-13 08:58:00 +0200, 2012-09-13 08:59:00 +0200,
2012-09-13 09:00:00 +0200]

Kind regards

robert

On Thu, Sep 13, 2012 at 09:59:44PM +0900, Jermaine O. wrote:

Well I’m basically experimenting with a very simple sort of a
availability time checker.

So let’s say user A sets its availability from 08:00 - 12:00
Where I’m storing this in the database as: 800…1200 for that user.

Databases support native time columns and the various Ruby db connection
libraries will serialize these as actual Time objects.

On Thu, Sep 13, 2012 at 10:07:03PM +0900, Robert K. wrote:

We can use Fixnum#divmod here

Indeed. I didn’t know about divmod.

For times I’d probably rather use Time though:

You are correct of course, but it can be encouraging as a beginner to
receive a direct answer rather than the right answer.

On Thu, Sep 13, 2012 at 1:54 PM, Matthew K. [email protected]
wrote:

    r = (@hours <=> o.hours)
    end

range = HoursMinutes.new(8,0)..HoursMinutes.new(9,0)
p range.to_a

The spaceship operator will break if o doesn’t quack like an
HoursMinutes object, and it’s not very useful, but it might be a
decent starting place.

Your #succ method does not overflow. I would make the class immutable
since that has some advantages. Here’s a different solution which
also includes range checking and basic math.

class HoursMinute
attr_reader :hour, :minute

def initialize(hr, mn)
raise ArgumentError, “Out of range: %2d:%02d” % [hr, mn] unless
(0…23).include?(hr) && (0…59).include?(mn)
@hour = hr.to_int
@minute = mn.to_int
end

def to_s; “%2d:%02d” % [hour, minute] end

def succ; self + 1 end

def +(min)
hr, mn = (hour * 60 + minute + min.to_int).divmod 60
self.class.new(hr % 24, mn)
end

def -(min) self + -min end

def hash; hour * 60 + minute end
def eql?(o) hour == o.hour && minute == o.minute end

def <=>(o) (hour <=> o.hour).nonzero? || minute <=> o.minute end
end

Kind regards

robert

On 13 September 2012 23:43, Robert K. [email protected]
wrote:

Your #succ method does not overflow. I would make the class immutable
since that has some advantages.

Actually my #succ did overflow, because the constructor used the
#minutes= method which, with the exception of a modulus error pointed
out earlier, overflowed into @hours. I agree, though, that making it
immutable would be easier. However this is all moot since it turns
out it’s a question of overlapping time ranges, which can be solved in
more interesting ways.

Jermaine: have you considered other options, such as decreasing the
granularity (e.g. using 10- or 15-minute blocks instead of 1-minute)?
Or combining the available and unavailable ranges without ever
discretising them?

My intuitive approach would be the latter. If I wasn’t about to get
my kids up and ready for school I’d probably hash out another
almost-correct algorithmic outline right now.

Cheers

On Thu, Sep 13, 2012 at 11:02 PM, Matthew K. [email protected]
wrote:

On 13 September 2012 23:43, Robert K. [email protected] wrote:

Your #succ method does not overflow. I would make the class immutable
since that has some advantages.

Actually my #succ did overflow, because the constructor used the
#minutes= method which, with the exception of a modulus error pointed
out earlier, overflowed into @hours. I agree, though, that making it
immutable would be easier.

Oh, I am sorry, I overlooked that.

However this is all moot since it turns
out it’s a question of overlapping time ranges, which can be solved in
more interesting ways.

Still these types of exercise train our Ruby skills so it wasn’t
completely in vain. :slight_smile:

Btw, where did it turn out? I can’t see it on the mailing list.

My intuitive approach would be the latter. If I wasn’t about to get
my kids up and ready for school I’d probably hash out another
almost-correct algorithmic outline right now.

:slight_smile:

Kind regards

robert