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!
on 2012-09-13 13:31
on 2012-09-13 13:38
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. <lists@ruby-forum.com> wrote: > Whereas I would like the output to be like this: > -- > Posted via http://www.ruby-forum.com/. > -- Matthew Kerwin, 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 2012-09-13 13:55
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 Kerwin <matthew@kerwin.net.au>
wrote:
>> I'm looking for a way to create an array out of a range, but does the
>>
>
>
>
> --
> Matthew Kerwin, 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 Kerwin, 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 2012-09-13 14:19
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 :-) 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 Kerwin 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
on 2012-09-13 14:43
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]
on 2012-09-13 14:46
Hi Sung,
(800..900).reject { |n| (n % 100) > 59 } is just what I need. Sweet!
Thanks a bunch
Sung Pae 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]
on 2012-09-13 14:47
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 2012-09-13 14:50
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
on 2012-09-13 14:59
@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 2012-09-13 15:08
On Thu, Sep 13, 2012 at 2:42 PM, Sung Pae <sungpae@gmail.com> 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 2012-09-13 15:10
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 2012-09-13 15:14
On Thu, Sep 13, 2012 at 10:07:03PM +0900, Robert Klemme 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 2012-09-13 15:43
On Thu, Sep 13, 2012 at 1:54 PM, Matthew Kerwin <matthew@kerwin.net.au> 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 2012-09-13 23:03
On 13 September 2012 23:43, Robert Klemme <shortcutter@googlemail.com> 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 2012-09-14 08:57
On Thu, Sep 13, 2012 at 11:02 PM, Matthew Kerwin <matthew@kerwin.net.au> wrote: > On 13 September 2012 23:43, Robert Klemme <shortcutter@googlemail.com> 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. :-) 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. :-) Kind regards robert
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.