I have a class with two attributes saving weekdays with numeric values. I had hoped to be able to use Enum, but appearantly you can not use the same value for two attributes with Enum.
How could I represent the integer value of an attribute to the corresponding weekday?
0 => "monday"
4 => "friday"
Date::DAYNAMES[(i + 1) % 7]
where i is your integer
2.0.0-p247 :001 > Date::DAYNAMES[(0 + 1) % 7]
=> "Monday"
2.0.0-p247 :002 > Date::DAYNAMES[(4 + 1) % 7]
=> "Friday"
2.0.0-p247 :003 > Date::DAYNAMES[(6 + 1) % 7]
=> "Sunday"
You cannot do a straight look up on the index because in DAYNAMES 0 is Sunday and you want 0 to be Monday.
Related
I need to translate a number of months, integer based, into a human friendly string containing information about years. its not easy to explain so I will just provide examples. inputs and outputs I want are:
input: 19
output: "1 year, 7 months"
input: 24
output: "2 years"
input: 26
output: "2 years, 2 months"
do you know any out of the box solutions? if not, how would you implement it yourself?
input = 26
year, month = input.divmod(12)
if month.eql? 0 and year > 1
puts "#{year} years"
elsif month.eql? 0 and year.eql? 1
puts "#{year} year"
elsif year > 1
puts "#{year} years, #{month} month"
else
puts "#{year} year, #{month} month"
end
Output
2 years, 2 month
Since this question is tagged with Ruby on Rails, ActiveSupport extensions are available, so this works too:
number_of_months = 19 # for example
ActiveSupport::Duration.build(number_of_months.months).inspect.gsub(' and',', ')
Edit
I just noticed that there was a bug in ActiveSupport::Duration version 6.0.2.2 that was fixed sometime prior to version 6.1.0.alpha that caused rounding errors for certain values of number_of_months.
Just for fun:
num.divmod(12).then { |y, m| [y, m].zip ['years', 'months'] }
.reject{ |e| e.first.zero? }
.each{ |e| e.last.delete_suffix!('s') if e.first == 1 }
.join(' ')
.tap{ |res| res.replace('0 months') if res.empty? }
Samples:
[11, 12, 13, 23, 24, 25, 26].each { |n| p [n, n.divmod(12).then.......] } # pseudo
[11, "11 months"]
[12, "1 year"]
[13, "1 year 1 month"]
[23, "1 year 11 months"]
[24, "2 years"]
[25, "2 years 1 month"]
[26, "2 years 2 months"]
def pluralize(num, string)
[num, (num == 1 ? string : "#{string}s")] * ' '
end
def humanize_months(months)
months = input % 12
years = input / 12
text = []
text << pluralize(years, 'year') if years > 0
text << pluralize(months, 'month') if months > 0
text * ', '
end
The integer variables are:
toonie = 2, loonie = 1, quarter = 1, dime = 0, nickel = 1, penny = 3
I want the final output to be
"2 toonies, 1 loonie, 1 quarter, 1 nickel, 3 pennies"
Is there a way to interpolate this all from Ruby code inside [] array brackets and then add .join(", ")?
Or will I have to declare an empty array first, and then write some Ruby code to add to the array if the integer variable is greater than 0?
I would do something like this:
coins = { toonie: 2, loonie: 1, quarter: 1, dime: 0, nickel: 1, penny: 3 }
coins.map { |k, v| pluralize(v, k) if v > 0 }.compact.join(', ')
#=> "2 toonie, 1 loonie, 1 quarter, 1 nickel, 3 penny"
Note that pluralize is a ActionView::Helpers::TextHelper method. Therefore it is only available in views and helpers.
When you want to use your example outside of views, you might want to use pluralize from ActiveSupport instead - what makes the solution slightly longer:
coins.map { |k, v| "#{v} #{v == 1 ? k : k.pluralize}" if v > 0 }.compact.join(', ')
#=> "2 toonie, 1 loonie, 1 quarter, 1 nickel, 3 penny"
Can be done in rails:
hash = {
"toonie" => 2,
"loonie" => 1,
"quarter" => 1,
"dime" => 0,
"nickel" => 1,
"penny" => 3
}
hash.to_a.map { |ele| "#{ele.last} #{ele.last> 1 ? ele.first.pluralize : ele.first}" }.join(", ")
Basically what you do is convert the hash to an array, which will look like this:
[["toonie", 2], ["loonie", 1], ["quarter", 1], ["dime", 0], ["nickel", 1], ["penny", 3]]
Then you map each element to the function provided, which takes the inner array, takes the numeric value in the last entry, places it in a string and then adds the plural or singular value based on the numeric value you just checked. And finally merge it all together
=> "2 toonies, 1 loonie, 1 quarter, 1 nickel, 3 pennies"
I'm not sure what exactly you're looking for, but I would start with a hash like:
coins = {"toonie" => 2, "loonie" => 1, "quarter" => 1, "dime" => 0, "nickel" => 1, "penny" => 3}
then you can use this to print the counts
def coin_counts(coins)
(coins.keys.select { |coin| coins[coin] > 0}.map {|coin| coins[coin].to_s + " " + coin}).join(", ")
end
If you would like appropriate pluralizing, you can do the following:
include ActionView::Helpers::TextHelper
def coin_counts(coins)
(coins.keys.select { |coin| coins[coin] > 0}.map {|coin| pluralize(coins[coin], coin)}).join(", ")
end
This is just for fun and should not be used in production but you can achieve it like
def run
toonie = 2
loonie = 1
quarter = 1
dime = 0
nickel = 1
penny = 3
Kernel::local_variables.each_with_object([]) { |var, array|
next if eval(var.to_s).to_i.zero?
array << "#{eval(var.to_s)} #{var}"
}.join(', ')
end
run # returns "2 toonie, 1 loonie, 1 quarter, 1 nickel, 3 penny"
The above does not implement the pluralization requirement because it really depends if you will have irregular plural nouns or whatever.
I would go with a hash solution as described in the other answers
Consider i have a string like this:
"1 hour 7 mins"
I need to extract number of hour (1) and min (7). the problem is either hour or mins can be nill so in this case the string would be 1 hour ot just 7 mins
I am mostly interested in regular expression. I have already seen this and run this code
result = duration.gsub(/[^\d]/, '')
result[0]!= nil ? hour=result[0] : hour=0
result[1]!=nil ? mins=result[1] : mins=0
the problem is, when i have only 5 mins it gives me 5 and i do not know it is mins or hour
So how can i do it?
What do you think about something like this:
hours = duration.match(/[\d]* hour/).to_s.gsub(/[^\d]/, '')
minutes = duration.match(/[\d]* mins/).to_s.gsub(/[^\d]/, '')
You could do that :
a = duration[/(\d*)(\s*hour)?s?\s*(\d*)(\s*min)?s?/][0]
if a.include?("hour")
hour = a[0]
min = a[2]
else
min = a[0]
end
Improved, this is what I wanted :
capture = duration.match(/^((\d*) ?hour)?s? ?((\d*) ?min)?s?/)
hour = capture[2]
min = capture[4]
You can try the regex here :
http://rubular.com/r/ACwfzUIHBo
I couldn't resist a bit of code golf:
You can do:
hours,_,mins = (duration.match /^([\d]* h)?([^\d]*)?([\d]* m)?/)[1..3].map(&:to_i)
Explanation:
matches number then 'h', then anything not a number, then number then 'm'. Then gets the match data and does .to_i (which in ruby if it starts with a number uses this number). It then assigns 1st and third match to hours and minutes respectively:
Output:
2.2.1 :001 > duration = "5 hours 26 min"
=> "5 hours 26 min"
2.2.1 :002 > hours,_,mins = (duration.match /^([\d]* h)?([^\d]*)?([\d]* m)?/)[1..3].map(&:to_i)
=> [5, 0, 26]
2.2.1 :003 > hours
=> 5
2.2.1 :004 > mins
=> 26
2.2.1 :005 > duration = "5 hours"
=> "5 hours"
2.2.1 :006 > hours,_,mins = (duration.match /^([\d]* h)?([^\d]*)?([\d]* m)?/)[1..3].map(&:to_i)
=> [5, 0, 0]
2.2.1 :007 > duration = "54 mins"
=> "54 mins"
2.2.1 :008 > hours,_,mins = (duration.match /^([\d]* h)?([^\d]*)?([\d]* m)?/)[1..3].map(&:to_i)
=> [0, 0, 54]
2.2.1 :009 >
I have a bit of broken legacy code I have to fix. Its purpose was to take a large array and return elements that have a particular sector number.
Here's a simplified version of the code in the app. Goal: return any instance of 1 or 3 in array:
array = [1,1,2,2,3,3].select{|num| num == (1 || 3) }
But the return value is simply #=> [1, 1] when the desired return was #=> [1, 1, 3, 3]
Basically, what I'm looking for is the Ruby equivalent to the following SQL query:
SELECT num FROM array
WHERE num IN (1, 3);
Ruby 1.8.7, Rails 2.3.15
Do as below to meet your need :
array = [1,1,2,2,3,3]
array.select{|num| [1,3].include? num }
# => [1, 1, 3, 3]
See why you got only [1,1].
1 || 3 # => 1
1 || 3 will always returns 1, thus num == 1 is evaluated as true when select is passing only 1. As a result you got [1,1].
I'm pretty new to Rails, I'm passing 2 variables to a view, in my controller have them defined like
#correct = 5
#total = 40
in my view I'm trying to mark them up like:
Score = <%=#score%>/<%=#total%> = <%=(#score/#total)%>
It outputs 0 for the division. Do I need to explicitly define that equation to output a float or something? How do I get it to output 0.125 instead of 0
Thanks guys
Decide whether you want to use #correct or #score. Also to use double division instead of integer multiply #score by 1.0:
<%=(1.0 * #score/#total)%>
Or alternatively cast #score to float:
<%=(#score.to_f/#total)%>
You need to explicitly convert your integers into floats:
1.9.3p0 :001 > a = 5
=> 5
1.9.3p0 :002 > b = 40
=> 40
1.9.3p0 :003 > a / b
=> 0
1.9.3p0 :005 > (a / b).to_f
=> 0.0
1.9.3p0 :006 > a.to_f / b.to_f
=> 0.125
In your case:
Score = <%=#score%>/<%=#total%> = <%=(#score.to_f/#total.to_f)%>