String concatenation of interpolated variables - ruby

The code below is longer than the recommended 80 characters.
I've tried to break it up and concatenate it with + operator and << operator, but it doesn't seem to work. I've tried the .to_s method and moved the colons into their own string but to no avail.
What would be the correct way to split this up onto 2 or 3 lines without getting the no method "+#":String error?
string = "#{sprintf("%02i", hours)}:#{sprintf("%02i", minutes)}:#{sprintf("%02i", seconds)}"

You can combine the three sprintf into one:
string = sprintf("%02i:%02i:%02i", hours, minutes, seconds)

To split string into multiline in Ruby you use backslash:
string = "#{sprintf("%02i", hours)}: \
#{sprintf("%02i", minutes)}: \
#{sprintf("%02i", seconds)}"
Notice how there is only one opening and one closing ".
You can also consider moving three calls to sprintf method into one.

I'm guessing that your real question is that, given numbers of seconds or a time object, you want to construct a string with the desired format.
If you are given seconds:
def fmt(seconds)
hours, minutes = seconds.divmod(60)
"%02d, %02d, %02d" % [hours, *minutes.divmod(60)]
end
fmt(223)
#=> "03, 00, 43"
If you are given a Time object:
require 'time'
def fmt(time)
time.strftime('%H hours, %M minutes, %S seconds')
end
time = Time.now
#=> 2015-05-16 14:38:02 -0700
fmt(time)
#=> "14 hours, 38 minutes, 02 seconds"
In this second case I added the words "hours", "minutes" and "seconds" just to illustrate how the format string could be modified.
If you want to print this on multiple lines, simply insert newlines (\n) in the appropriate format string.

Related

Split string with Ruby regex

We have a CSV file. A specific column was to have many entries and those were to be separated with a newline character "\n", which didn't happen. Sample data of the affected column are as follows.
30 units - R135 50 units - R225 80 units - R360
Is there a method to split this into three columns? The number of columns is not fixed. I guess we will have to use many space characters (more than two) to split entries. I need:
data = "that long string"
# Some operation
result = ["30 units - R135", "50 units - R225", "80 units - R360"]
I tried some thing like this. But it didn't work. The result was the same string.
a.split('/(\s){3,}/')
What approach could be taken to split the data?
A statement like
a.split('/(\s){3,}/')
will split the string in a at occurrences of the string /(\s){3,}/, which (unsurprisingly) occurs nowhere in the target string, so it remains instact.
You need to specify a regexp by writing
data = '30 units - R135 50 units - R225 80 units - R360'
result = data.split /\s{3,}/
p result
output
["30 units - R135", "50 units - R225", "80 units - R360"]
the correct regex for this is:
a.split(/\s{3,}/)
a nice place to try out regex expressions:
http://rubular.com/ (you may not need it , but i love it too much so sharing :) )

Convert 12-hour time to 24-hour time with a regex

I have a bunch of strings with opening hours in this format:
Mon-Fri: AM7:00-PM8:00\nSat-Sun: AM8:00-PM6:00
I can deal with the "AM" part by just removing it, but I'd like to convert the PM by
Removing "PM"
Adding 12 to the number before the ":"
Taking care of the fact that PM is sometimes double-digits (e.g. PM11:00)
There can be zero or more PM times in the string.
I'm not sure how to manipulate the time as a number. I've gotten this far:
opening_hours.sub! /PM([\d]?[\d]):/, "***\1***"
Which outputs things like this:
AM7:15-***\u0001***00
The '\u0001` may be due to Japanese characters in the string.
You can take advantage of the fact that String#gsub accepts a block. Something like this will do for you?
s = "Mon-Fri: AM7:00-PM8:00\nSat-Sun: AM8:00-PM11:00"
s2 = s.gsub('AM', '').gsub(/PM(\d+)/) do |match|
(match.gsub('PM', '').to_i + 12).to_s
end
s2 # => "Mon-Fri: 7:00-20:00\nSat-Sun: 8:00-23:00"
Have a look at this question, ruby has a class called datatime.
Convert 12 hr time to 24 hr format in Ruby

Slice the last 4 or otherwise handle this input/output pattern, in Ruby?

print('What is the day and hour (ex., Monday 08AM): ')
appoint = gets.slice[0..-4]
puts(appoint)
is returning this error:
/scope.rb:2:in slice': wrong number of arguments (0 for 1..2) (ArgumentError)
from /scope.rb:2:in'
Also tried slice[appoint.length..-4] and some other things.
From reading other questions, I gathered that this was how such a slice was done. I'm not familiar with the regex pattern. I'd actually like to be able tot return the day of the week, as well, which may mean from -5 back to input or everything up until the space with some kind of regex pattern.
Do you want this ?
appoint = gets.slice(-4,4)
For Monday 08AM it returns:
08AM
You can use slice like this: slice(start, length).
In your case start is -4 and length is 4.
EDIT
Or with only brackets:
appoint = gets[-4..-1]
A string is also an array of characters.
Regex exmaple:
s = "Monday 08AM"
day = /[a-zA-Z]+/
s[day]
=> "Monday"
# \d? to also catch 8AM without 0 at the start
hour = /\d?\d[paPA][mM]/
s[hour]
=> "08AM"
Regex tutorial from Ruby 1.9.3 docs

How do you store a string in MongoDB as a Date type using Ruby?

I have a string that I'm parsing out from log files that looks like the following:
"[22/May/2011:23:02:21 +0000]"
What's the best way (examples in Ruby would be most appreciated, as I'm using the Mongo Ruby driver) to get that stashed into MongoDB as a native Date type?
require 'date' # this is just to get the ABBR_MONTHNAMES list
input = "[22/May/2011:23:02:21 +0000]"
# this regex captures the numbers and month name
pattern = %r{^\[(\d{2})/(\w+)/(\d{4}):(\d{2}):(\d{2}):(\d{2}) ([+-]\d{4})\]$}
match = input.match(pattern)
# MatchData can be splatted, which is very convenient
_, date, month_name, year, hour, minute, second, tz_offset = *match
# ABBR_MONTHNAMES contains "Jan", "Feb", etc.
month = Date::ABBR_MONTHNAMES.index(month_name)
# we need to insert a colon in the tz offset, because Time.new expects it
tz = tz_offset[0,3] + ':' + tz_offset[3,5]
# this is your time object, put it into Mongo and it will be saved as a Date
Time.new(year.to_i, month, date.to_i, hour.to_i, minute.to_i, second.to_i, tz)
A few things to note:
I assumed that the month names are the same as in the ABBR_MONTHNAMES list, otherwise, just make your own list.
Never ever use Date.parse to parse dates it is incredibly slow, the same goes for DateTime.parse, Time.parse, which use the same implementation.
If you parse a lot of different date formats check out the home_run gem.
If you do a lot of these (like you often do when parsing log files), consider not using a regex. Use String#index, #[] and #split to extract the parts you need.
If you want to do this as fast as possible, something like the following is probably more appropriate. It doesn't use regexes (which are useful, but not fast):
date = input[1, 2].to_i
month_name = input[4, 3]
month = Date::ABBR_MONTHNAMES.index(month_name)
year = input[8, 4].to_i
hour = input[13, 2].to_i
minute = input[16, 2].to_i
second = input[19, 2].to_i
tz_offset = input[22, 3].to_i * 60 * 60 + input[25, 2].to_i * 60
Time.new(year, month, date, hour, minute, second, tz_offset)
It takes advantage of the fact that all fields have fixed width (at least I assume they do). So all you need to do is extract the substrings. It also calculates the timezone offset as a number instead of a string.

Convert an integer into a signed string in Ruby

I have a report in which I'm listing total values and then changes in parentheses. E.g.:
Songs: 45 (+10 from last week)
So I want to print the integer 10 as "+10" and -10 as "-10"
Right now I'm doing
(song_change >= 0 ? '+' : '') + song_change.to_s
Is there a better way?
"%+d" % song_change
String#% formats the right-hand-side according to the print specifiers in the string. The print specifier "%d" means decimal aka. integer, and the "+" added to the print specifier forces the appropriate sign to always be printed.
You can find more about print specifiers in Kernel#sprintf, or in the man page for sprinf.
You can format more than one thing at once by passing in an array:
song_count = 45
song_change = 10
puts "Songs: %d (%+d from last week)" % [song_count, song_change]
# => Songs: 45 (+10 from last week)
You could add a method to Fixnum called to_signed_s, but that may be overkill. You would eliminate copying and pasting, however, which would be good.
Personall, I'd just write a StringUtil class to handle the conversion.
Alternatively, a better OO solution would be to wrap the FixNum in a holder class and override THAT class's to_s.
IE: Create a class called SignedFixnum and wrap your Fixnum objects in it whenever they need to be signed.
Wayne already posted what I consider the best option, but here's another one just for fun...
"#{'+' if song_change >= 0}#{song_change}"
I think your original code is good, just extract it out into a helper so it doesn't clutter up your views and you don't have to repeat it each time that you want to use it.
Put it in your application_helper.rb file like this
def display_song_change
(song_change >= 0 ? '+' : '') + song_change.to_s
end

Resources