Ruby average value of array of time values (fixnums) - ruby-on-rails

Looking to get the average duration, where duration is in the form of 1.day, 3.months, 2.weeks format..
# provided array
a = [1.day, 3.days, 1.week, 4.days]
# desired output
a.average = "3 days"
Any way I have tried results in a number of seconds being the output.. for instance:
a.inject(:+) = "15 days"
a.inject(:+) / a.size = 324000
I've looked at the Linguistics gem, but it only outputs the value as a number (three hundred and twenty four thousand)

def average_days(a)
seconds = a.inject(:+) / a.size
minutes = seconds / 60
days = (minutes / 1440).round
"#{days} days"
end

> a = [1.day, 3.days, 1.week, 4.days]
> (a.inject(0.0) {|sum, n| sum + n} / a.size) / (60 * 60 * 24)
=> 3.75
If you insist. Round and/or truncate however you want.
((a.inject(0.0) {|sum, n| sum + n} / a.size) / (60 * 60 * 24)).days

Related

Need to convert to integer and then sum of the Plucked data ruby 2.6

I need SUM of the plucked timings.
I did
#dailystatus_infos.task_times.pluck(:total_min)
I got following
["00:00:00", "1:52:00", "00:00:00", "00:02:28", "1:54:00"]
output. [Hour:Minute:Second] format
Now I need to convert those Minutes to integer and sum of it.
I need SUM of the plucked timings
I'd start by writing a helper method to convert the hh:mm:ss string to seconds. A regular expression would work:
def to_seconds(string)
string.match(/(?<hours>\d+):(?<minutes>\d+):(?<seconds>\d+)/) do |m|
m[:hours].to_i * 3600 + m[:minutes].to_i * 60 + m[:seconds].to_i
end
end
to_seconds('00:00:12') #=> 12
to_seconds('00:01:00') #=> 60
to_seconds('00:01:12') #=> 72
Now you can sum the seconds via:
total_mins = ["00:00:00", "1:52:00", "00:00:00", "00:02:28", "1:54:00"]
total_mins.sum { |str| to_seconds(str) }
#=> 13708
And, if necessary, convert that back to h:mm:ss via divmod:
seconds = 13708
hours, seconds = seconds.divmod(3600)
minutes, seconds = seconds.divmod(60)
format('%d:%02d:%02d', hours, minutes, seconds)
#=> "3:48:28"
We first determine the total seconds:
arr = ["00:00:00", "1:52:00", "00:00:00", "00:02:28", "1:54:00"]
s = arr.sum do |str|
str.split(':').reduce(0) { |t, s| t * 60 + s.to_i }
end
#=> 13708
and then manipulate s as desired. The number of minutes, for example, equals
s.fdiv(60)
#=> 228.46666666666667
which might be rounded or truncated.
Something like this
total_minutes = #dailystatus_infos.task_times.pluck(:total_min)
total_minutes.map { |t| (::Time.parse(t).seconds_since_midnight / 60).to_i }.sum
arr = ["00:00:00", "1:52:00", "00:00:00", "00:02:28", "1:54:00"]
arr.map { |t| ::Time.parse(t).seconds_since_midnight / 60 }.sum.to_i
=> 228
You get the minute by splitting on :. Then map and sum.
ary = ["00:00:00", "1:52:00", "00:00:00", "00:02:28", "1:54:00"]
ary.map{ |s| s.split(':')[1].to_i }.sum #=> 108 (0 + 52 + 0 + 2 + 54)

Ruby Program time conversion

The task is to Write a method that will take in a number of minutes, and returns a string that formats the number into hours:minutes.
here's what I have so far:
def time_conversion(minutes)
minutes = (minutes / 60) % 60
hours = minutes / (60 * 60)
format(" %02d:%02d ", hours, minutes)
return format
end
it's not working out for me
Try this
def time_conversion(time)
minutes = time % 60
hours = time / 60
minutes = (minutes < 10)? "0" + minutes.to_s : minutes.to_s
return hours.to_s + ":" + minutes
end
Using division in Ruby returns a whole number, lowered to the previous number. Using modulus returns the remainder after division.
Ruby's Numeric#divmod is exactly what you want here. It returns both the quotient and remainder of a division operation, so e.g. 66.divmod(60) returns [ 1, 6 ]. Combined with sprintf (or String#%, it makes for an extremely simple solution:
def time_conversion(minutes)
"%02d:%02d" % minutes.divmod(60)
end
puts time_conversion(192)
# => 03:12
Well try
h = minutes/60
M = minutes%60

Converting hours, minutes and seconds to seconds in ruby

I was wondering how to safely convert HH:MM:SS to seconds in ruby, given the input will only be in HH:MM:SS (or H:M:S), without days.
Days would have to be in hours already - "168:00:00" instead of "7 days, 00:00:00".
'12:34:56'.split(':').map(&:to_i).inject(0) { |a, b| a * 60 + b }
=> 45296
h, m, s = "168:00:00".split(":").map(&:to_i)
h %= 24
(((h * 60) + m) * 60) + s
The solution is quite simple:
secs = 0
=> 0
"12:34:56".split(":").reverse.each_with_index do |x,y|
"#{secs} += (#{x.to_i})*(#{60**y})"
secs += (x.to_i)*(60**y)
end
=> ["56", "34", "12"]
secs
=> 45296
No use of Time or DateTime and no timezone problems!
Cheers!

Ruby/Rails - How to convert seconds to time?

I need to perform the following conversion:
0 -> 12.00AM
1800 -> 12.30AM
3600 -> 01.00AM
...
82800 -> 11.00PM
84600 -> 11.30PM
I came up with this:
(0..84600).step(1800){|n| puts "#{n.to_s} #{Time.at(n).strftime("%I:%M%p")}"}
which gives me the wrong time, because Time.at(n) expects n to be number of seconds from epoch:
0 -> 07:00PM
1800 -> 07:30PM
3600 -> 08:00PM
...
82800 -> 06:00PM
84600 -> 06:30PM
What would be the most optimal, time zone independent solution for this transformation?
The simplest one-liner simply ignores the date:
Time.at(82800).utc.strftime("%I:%M%p")
#-> "11:00PM"
Not sure if this is better than
(Time.local(1,1,1) + 82800).strftime("%I:%M%p")
def hour_minutes(seconds)
Time.at(seconds).utc.strftime("%I:%M%p")
end
irb(main):022:0> [0, 1800, 3600, 82800, 84600].each { |s| puts "#{s} -> #{hour_minutes(s)}"}
0 -> 12:00AM
1800 -> 12:30AM
3600 -> 01:00AM
82800 -> 11:00PM
84600 -> 11:30PM
Stephan
Two offers:
The elaborate DIY solution:
def toClock(secs)
h = secs / 3600; # hours
m = secs % 3600 / 60; # minutes
if h < 12 # before noon
ampm = "AM"
if h = 0
h = 12
end
else # (after) noon
ampm = "PM"
if h > 12
h -= 12
end
end
ampm = h <= 12 ? "AM" : "PM";
return "#{h}:#{m}#{ampm}"
end
the Time solution:
def toClock(secs)
t = Time.gm(2000,1,1) + secs # date doesn't matter but has to be valid
return "#{t.strftime("%I:%M%p")} # copy of your desired format
end
HTH
In other solutions, the hour-counter would be reset to 00 when crossing 24-hour day boundaries. Also beware that Time.at rounds down, so it will give the wrong result if the input has any fractional seconds (f.ex. when t=479.9 then Time.at(t).utc.strftime("%H:%M:%S") will give 00:07:59 and not 00:08:00` which is the correct one).
If you want a way to convert any number of seconds (even high counts larger than 24-hour day spans) into an ever increasing HH:MM:SS counter, and handle potential fractional seconds, then try this:
# Will take as input a time in seconds (which is typically a result after subtracting two Time objects),
# and return the result in HH:MM:SS, even if it exceeds a 24 hour period.
def formatted_duration(total_seconds)
total_seconds = total_seconds.round # to avoid fractional seconds potentially compounding and messing up seconds, minutes and hours
hours = total_seconds / (60*60)
minutes = (total_seconds / 60) % 60 # the modulo operator (%) gives the remainder when leftside is divided by rightside. Ex: 121 % 60 = 1
seconds = total_seconds % 60
[hours, minutes, seconds].map do |t|
# Right justify and pad with 0 until length is 2.
# So if the duration of any of the time components is 0, then it will display as 00
t.round.to_s.rjust(2,'0')
end.join(':')
end
Modified from #springerigor's and suggestion in the discussion at https://gist.github.com/shunchu/3175001

Rails: distance_of_time_NOT_in_words

As in, distance_of_time(Time.now, Time.tomorrow).days = 1 or something along those lines? If not, what would be a good way to achieve this? I know there is "from_now" but why wouldn't there be a from_whenever?
I don't know of a built-in general solution but if it's only days you need to compare, you can do
d = DateTime.now
d2 = DateTime.now.advance(:days => 1)
days_diff = (d2-d).to_i # i.e., 1
Using Times you can do the same thing for seconds. From those seconds you can build a reasonable models of weeks, days, hours and minutes difference:
diff = (Time.now - Time.now.advance(:days => 38, :hours=>2, :minutes => 23)).abs
day_diff = diff % 1.week.seconds
weeks = (diff - day_diff) / 1.week.seconds
hour_diff = day_diff % 1.day.seconds
days = (day_diff - hour_diff) / 1.day.seconds
minute_diff = hour_diff % 1.hour.seconds
hours = (hour_diff - minute_diff) / 1.hour.seconds
second_diff = minute_diff % 1.minute.second
minutes = (minute_diff - second_diff) / 1.minute.seconds
fractions = second_diff % 1
seconds = (second_diff - fractions)
s=Struct.new(:weeks, :days, :hours, :minutes, :seconds)
s.new(weeks, days, hours, minutes, seconds)
s.weeks # 5
s.days # 2
s.minutes # 23
s.seconds # 0
(or something like this, I haven't really tested the code but you get the idea - I hope).

Resources