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
Related
The following code always outputs "not":
print "input a number please. "
TestNumber = gets
if TestNumber % 2 == 0
print "The number is even"
else
print "The number is not even"
end
What is going wrong with my code?
The gets() method returns an object of type String.
When you call %() on a String object, the return value is a new String object (usually it changes the text. You can read more about string formatting here).
Since there are no String objects that == 0, the if/else will always take the same path.
If you want to use the return value of gets() like a number, you will need to transform it into one first. The simplest approach is probably to use the to_i() method on String objects, which returns a new 'Integer' object. If you're doing something where the user input will not always be an integer (e.g. 3.14 or 1.5), you might need to use a different approach.
One last thing: in your example the result of gets() is saved into a constant called TestNumber. Constants are different to normal variables, and they will probably cause problems if you're not using them intentionally. Normal variables don't start with capital letters. (You can read more about ruby variables here). In ruby you need to write you variable names like this: test_number.
I suspect your Testnumber variable might be interpreted as a string during the operation. make sure the testnum is converted to an integer first even if you put in say 100 it could be its being interpreted as the stirng "100" and not the integer 100.
A similar issue can be found here: Ruby Modulo Division
You have to convert TestNumber from string to integer, as your input has linefeed and/or other unwanted characters that do not match an integer.
Use TestNumber = gets.to_i to convert to integer before testing.
I've been working with the Ruby chr and ord methods recently and there are a few things I don't understand.
My current project involves converting individual characters to and from ordinal values. As I understand it, if I have a string with an individual character like "A" and I call ord on it I get its position on the ASCII table which is 65. Calling the inverse, 65.chr gives me the character value "A", so this tells me that Ruby has a collection somewhere of ordered character values, and it can use this collection to give me the position of a specific character, or the character at a specific position. I may be wrong on this, please correct me if I am.
Now I also understand that Ruby's default character encoding uses UTF-8 so it can work with thousands of possible characters. Thus if I ask it for something like this:
'好'.ord
I get the position of that character which is 22909. However, if I call chr on that value:
22909.chr
I get "RangeError: 22909 out of char range." I'm only able to get char to work on values up to 255 which is extended ASCII. So my questions are:
Why does Ruby seem to be getting values for chr from the extended ASCII character set but ord from UTF-8?
Is there any way to tell Ruby to use different encodings when it uses these methods? For instance, tell it to use ASCII-8BIT encoding instead of whatever it's defaulting to?
If it is possible to change the default encoding, is there any way of getting the total number of characters available in the set being used?
According to Integer#chr you can use the following to force the encoding to be UTF_8.
22909.chr(Encoding::UTF_8)
#=> "好"
To list all available encoding names
Encoding.name_list
#=> ["ASCII-8BIT", "UTF-8", "US-ASCII", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "UTF-16", "UTF-32", ...]
A hacky way to get the maximum number of characters
2000000.times.reduce(0) do |x, i|
begin
i.chr(Encoding::UTF_8)
x += 1
rescue
end
x
end
#=> 1112064
After tooling around with this for a while, I realized that I could get the max number of characters for each encoding by running a binary search to find the highest value that doesn't throw a RangeError.
def get_highest_value(set)
max = 10000000000
min = 0
guess = 5000000000
while true
begin guess.chr(set)
if (min > max)
return max
else
min = guess + 1
guess = (max + min) / 2
end
rescue
if min > max
return max
else
max = guess - 1
guess = (max + min) / 2
end
end
end
end
The value input to the method is the name of the encoding being checked.
For no particular reason, I am trying to add a #reverse method to the Integer class:
class Integer
def reverse
self.to_s.reverse.to_i
end
end
puts 1337.reverse # => 7331
puts 1000.reverse # => 1
This works fine except for numbers ending in a 0, as shown when 1000.reverse returns 1 rather than 0001. Is there any way to keep leading zeroes when converting a string into an integer?
Short answer: no, you cant.
2.1.5 :001 > 0001
=> 1
0001 doesn't make sense at all as Integer. In the Integer world, 0001 is exactly as 1.
Moreover, the number of leading integer is generally irrelevant, unless you need to pad some integer for displaying, but in this case you are probably converting it into another kind of object (e.g a String).
If you want to keep the integer as Fixnum you will not be able to add leading zeros.
The real question is: why do you want/need leading zeros? You didn't provide such information in the question. There are probably better ways to achieve your result (such as wrapping the value into a decorator object if the goal is to properly format a result for display).
Does rjust work for you?
1000.to_s.reverse.to_i.to_s.rjust(1000.to_s.size,'0') #=> "0001"
self.to_s.to_i does convert the integer to a string and this string "0001" to an integer value. Since leading zeros are not required for regular numbers they are dropped. In other words: Keeping leading zeros does not make sense for calculations, so they are dropped. Just ask yourself how the integer 1 would look like if leading zeros would be preserved, since it represents a 32 bit number. If you need the leading zeros, there is no way around a string.
BUT 10 + "0001".to_i returns 11, so you probably need to override the + method of the String class.
I was wondering what the difference between print x and print "#{x}", in Ruby was. Does it really matter which one we use?
The expression print "#{foo}" roughly translates to print foo.to_s.
Kernel#print is a thin wrapper around IO#print which ultimatively calls IO#write. From write's documentation:
[...] If the argument is not a string, it will be converted to a string using to_s. [...]
So in the end, there is close to no difference. print "#{foo}" will however first create a String representation of foo and secondly interpolate that result into an otherwise empty string—but I think that could (should) easily be optimized by the interpreter.
print "#{foo}" - here you are doing string interpolation.Whatever object will be referenced by foo(if it is a local variable), returned from foo(if it is a method), on that result String#to_s will be applied.
print foo will output the object will be referenced by foo(if it is a local variable), returned from foo(if it is a method), on that result #to_s will be applied.
There is no difference, they both apply to_s implicitly at some point. You should use print x and not print "#{x}". Why would you wonder which to use? print "#{x}" is obviously less simple than print x.
The print name is usually used if you only need to print that thing and nothing more.
String interpolation is used when you want to insert the values in other strings.
print "My name is #{my_name} and I am currently #{my_age} years old."
It is even possible to insert some logic:
print "My name is #{my_name.capitalize} and"
print "I am currently #{my_age} year#{my_age>1 ? 's':''} old." #print years instead of year if age is greater than 1.
I'm outputting a set of numbered files from a Ruby script. The numbers come from incrementing a counter, but to make them sort nicely in the directory, I'd like to use leading zeros in the filenames. In other words
file_001...
instead of
file_1
Is there a simple way to add leading zeros when converting a number to a string? (I know I can do "if less than 10.... if less than 100").
Use the % operator with a string:
irb(main):001:0> "%03d" % 5
=> "005"
The left-hand-side is a printf format string, and the right-hand side can be a list of values, so you could do something like:
irb(main):002:0> filename = "%s/%s.%04d.txt" % ["dirname", "filename", 23]
=> "dirname/filename.0023.txt"
Here's a printf format cheat sheet you might find useful in forming your format string. The printf format is originally from the C function printf, but similar formating functions are available in perl, ruby, python, java, php, etc.
If the maximum number of digits in the counter is known (e.g., n = 3 for counters 1..876), you can do
str = "file_" + i.to_s.rjust(n, "0")
Can't you just use string formatting of the value before you concat the filename?
"%03d" % number
Use String#next as the counter.
>> n = "000"
>> 3.times { puts "file_#{n.next!}" }
file_001
file_002
file_003
next is relatively 'clever', meaning you can even go for
>> n = "file_000"
>> 3.times { puts n.next! }
file_001
file_002
file_003
As stated by the other answers, "%03d" % number works pretty well, but it goes against the rubocop ruby style guide:
Favor the use of sprintf and its alias format over the fairly
cryptic String#% method
We can obtain the same result in a more readable way using the following:
format('%03d', number)
filenames = '000'.upto('100').map { |index| "file_#{index}" }
Outputs
[file_000, file_001, file_002, file_003, ..., file_098, file_099, file_100]