Ruby `downcase!` returns `nil` - ruby

With this code:
input = gets.chomp.downcase!
puts input
if there is at least one uppercase letter in the input, the input will be put on screen, freed of its uppercases. But if the input has no uppercase letter, it will put nil, like if nothing was written.
I want my input to be fully downcased; if it is a string with no uppercase letter, it should return the same string.
I thought about something like this:
input = gets.chomp
if input.include(uppercase) then input.downcase! end
But this doesn't work. I hope someone has an idea on how I should do this.

According to the docs for String:
(emphasis is mine added)
downcase
Returns a copy of str with all uppercase letters replaced with their lowercase counterparts. The operation is locale
insensitive—only characters “A” to “Z” are affected. Note: case
replacement is effective only in ASCII region.
downcase!
Downcases the contents of str, returning nil if no changes were made. Note: case replacement is effective only in ASCII
region.
Basically it says that downcase! (with exclamation mark) will return nil if there is no uppercase letters.
To fix your program:
input = gets.chomp.downcase
puts input
Hope that helped!

This will work:
input = gets.chomp.downcase
puts input
String#downcase
Returns a modified string and leaves the original unmodified.
str = "Hello world!"
str.downcase # => "hello world!"
str # => "Hello world!"
String#downcase!
Modifies the original string, returns nil if no changes were made or returns the new string if a change was made.
str = "Hello world!"
str.downcase! # => "hello world!"
str # => "hello world!"
str.downcase! # => nil
! (bang) methods
It's common for Ruby methods with ! / non-! variants to behave in a similar manner. See this post for an in-depth explanation why.

The reason that downcase! returns nil is so you know whether or not the object was changed. If you're assigning the modified string to another variable, like you are here, you should use downcase instead (without the bang !).
If you're not familiar, the standard library bang methods typically act on the receiver directly. That means this:
foo = "Hello"
foo.downcase!
foo #=> "hello"
Versus this:
foo = "Hello"
bar = foo.downcase
foo #=> "Hello"
bar #=> "hello"

Related

Why does `gsub!` return `nil`?

I am using a hash map to advance the character by position: "a" into "b", etc., and to capitalize vowels.
def LetterChanges(str)
str.to_s
puts str
h = ("a".."z").to_a
i = ("b".."z").to_a.push("a")
hash = Hash[h.zip i]
new_str = str.downcase.gsub(/[a-z]/,hash)
new_str.gsub!(/[aeiou]/) {|n| n.upcase }
end
LetterChanges("hello world")
LetterChanges("sentence")
LetterChanges("replace!*")
LetterChanges("coderbyte")
LetterChanges("beautiful^")
LetterChanges("oxford")
LetterChanges("123456789ae")
LetterChanges("this long cake#&")
LetterChanges("a b c dee")
LetterChanges("a confusing /:sentence:/[ this is not!!!!!!!~")
The above code works as expected except for the examples "replace!*" and "123456789ae", for which it returns nil. Why is this?
String#gsub! modifies the original string, and returns that string or nil if no replacements were performed.
String#gsub does not modify the original string but always return the result even if nothing was changed.
Either return new_str at the end of your method, or use gsub instead of gsub!.
This is somewhat of a pattern in Ruby - when multiple version of a method exist, the one with ! will modify the receiver and the one without will simply return the result.
As an aside, it looks like you're not using the result of str.to_s for anything. If you know it's a string, to_s is pointless. If it might not be, you should make use of the result, for example like so:
str = str.to_s
String#gsub! returns nil when no substitution is performed.
new_str.gsub!(/[aeiou]/) {|n| n.upcase }
returns nil when new_str doesn't contain any vowel letters. This is the case for example, if str is "replace!*", new_str is sfqmbdf!*, no vowels.

Why the newline char does not work here?

def say(arg)
"Hello, #{arg}.\n"
end
say("ABC") # => "Hello, ABC.\n"
Why here \n is printed instead of a newline?
because say returns a String. it doesn't print anything.
If you want to print something you should try:
def say(arg)
puts "Hello, #{arg}.\n"
end
You're most likely trying this in irb which displays the Ruby representation of a string. Compare:
irb(main):007:0> puts say("ABC") + say("ABC")
Hello, ABC.
Hello, ABC.
=> nil
irb(main):008:0> puts "ABC" + "ABC"
ABCABC
=> nil
It is actually never printed to stdio or anything like that, you are simply returning a string from a function.
I'll make the assumption that you are evaluating this in IRB, IRB prints out the result of every expression.
use puts or similar.

Ruby: Replace parts of a string

I have many strings following a certain pattern:
string = "Hello, #name. You did #thing." # example
Basically, my strings are a description where #word is dynamically. I need to replace each with a value at runtime.
string = "Hello, #{#name}. You did #{#thing}." # Is not an option!
The #word is basically a variable, but I just cannot use the method above.
How should I do that?
Instead doing search/replace, you can use Kernel#sprintf method, or its % shorthand. Combined with Hashes, it can come pretty handy:
'Hello, %{who}. You did %{what}' % {:who => 'Sal', :what => 'wrong'}
# => "Hello, Sal. You did wrong"
The advantage of using Hash instead of Array is that you don't have to worry about the ordering, and you can have the same value inserted on multiple places in the string.
You can format your string with placeholders that can be switched out dynamically using String's % operator.
string = "Hello, %s. You did %s"
puts string % ["Tony", "something awesome"]
puts string % ["Ronald", "nothing"]
#=> 'Hello, Tony. You did something awesome'
#=> 'Hello, Ronald. You did nothing'
Possible use case: Let's say you were writing a script that would be taking the name and action in as parameters.
puts "Hello, %s. You did %s" % ARGV
Assuming 'tony' and 'nothing' were the first two parameters, you would get 'Hello, Tony. You did nothing'.

resolve #{var} in string

I have loaded a string with #{variable} references in it. How would I resolve those variables in the string like puts does?
name="jim"
str="Hi #{name}"
puts str
Instead of puts, I would like to have the result available to pass as a parameter or save into a variable.
you could eval it
name = "Patrick"
s = 'hello, #{name}'
s # => "hello, \#{name}"
# wrap the string in double quotes, making it a valid interpolatable ruby string
eval "\"#{s}\"" # => "hello, Patrick"
puts doesn't resolve the variables. The Ruby parser does when it creates the string. if you passed str to any other method, it would be the same as passing 'Hi jim', since the interpolation is already done.
String has a format option that appears as %. It can be used to pass arguments into a predefined string much like interpolation does.
message = "Hello, %s"
for_patrick = message % "Patrick" #=> "Hello, Patrick"
for_jessie = message % "Jessie" #=> "Hello, Jessie"
messages = "Hello, %s and %s"
for_p_and_j = messages % ["Patrick", "Jessie"] #=> "Hello, Patrick and Jessie"
It may not look "Rubyish" but I believe it is the functionality you are looking for.
So, if you have a string coming in from somewhere that contains these placeholders, you can then pass in values as arguments as so:
method_that_gets_hello_message % "Patrick"
This will also allow you to only accept values you are expecting.
message = "I can count to %d"
message % "eleven" #=> ArgumentError: invalid value for Integer()
There's a list on Wikipedia for possible placeholders for printf() that should also work in Ruby.
The eval seems to be the only solution for this particular task. But we can avoid this dirty-unsafe-dishonourable eval if we modify the task a bit: we can resolve not local, but instance variable without eval using instance_variable_get:
#name = "Patrick"
#id = 2 # Test that number is ok
#a_b = "oooo" # Test that our regex can eat underscores
s = 'hello, #{name} !!#{id} ??#{a_b}'
s.gsub(/#\{(\w+)\}/) { instance_variable_get '#'+$1 }
=> "hello, Patrick !!2 ??oooo"
In this case you even can use any other characters instead of #{} (for example, %name% etc), by only modifying the regex a bit.
But of course, all this smells.
It sounds like you want the basis for a template system, which Ruby does easily if you use String's gsub or sub methods.
replacements = { '%greeting%' => 'Hello', '%name%' => 'Jim' }
pattern = Regexp.union(replacements.keys)
'%greeting% %name%!'.gsub(pattern, replacements)
=> "Hello Jim!"
You could just as easily define the key as:
replacements = { '#{name}' => 'Jim' }
and use Ruby's normal string interpolation #{...} but I'd recommend not reusing that. Instead use something unique.
The advantage to this is the target => replacement map can easily be put into a YAML file, or a database table, and then you can swap them out with other languages, or different user information. The sky is the limit.
The benefit to this also, is there is no evaluation involved, it's only string substitution. With a bit of creative use you can actually implement macros:
macros = { '%salutation%' => '%greeting% %name%' }
replacements = { '%greeting%' => 'Hello', '%name%' => 'Jim' }
macro_pattern, replacement_pattern = [macros, replacements].map{ |h| Regexp.union(h.keys) }
'%salutation%!'.gsub(macro_pattern, macros).gsub(replacement_pattern, replacements)
=> "Hello Jim!"

Method chaining chomp/chompbang

Why does chomp allow chaining but chomp! doesn't? For example:
"HELLO ".chomp.downcase
#> hello
"HELLO ".chomp!.downcase
#> nil
Another interesting example:
"100 ".chomp.to_i
#> 100
"100 ".chomp!.to_i
#> 0
Any ideas why this behavior occurs on the string, and why nil.to_i returns 0?
From the fine manual:
chomp(separator=$/) → new_str
Returns a new String with the given record separator removed from the end of str (if present). If $/ has not been changed from the default Ruby record separator, then chomp also removes carriage return characters (that is it will remove \n, \r, and \r\n).
and for chomp!:
chomp!(separator=$/) → str or nil
Modifies str in place as described for String#chomp, returning str, or nil if no modifications were made.
So neither chomp nor chomp! do what you think they do. Observe:
>> s = '100 '
=> "100 "
>> s.chomp
=> "100 "
>> s
=> "100 "
>> s.chomp!
=> nil
>> s
=> "100 "
So neither one cares about trailing spaces unless you tell them to, they just strip off trailing EOLs by default.
'100 '.chomp! returns nil because that's what the documentation says it does. No substitution was made so it returns nil.
Why does nil.to_i give you zero? Well, from the fine manual:
to_i → 0
Always returns zero.
That doesn't leave much room for ambiguity or interpretation.
I think you're actually after the strip family of methods rather than chomp:
String#lstrip
String#lstrip!
String#rstrip
String#rstrip!
String#strip
String#strip!
Those remove whitespace from the string.
Your question will vanish if you remember to provide an argument to chomp. Without one, chomp will only remove newlines and carriage returns, which are absent from your string. The bang augmented chomp returns nil because it hasn't done any modification (as per the documentation).
In brief, you really wanted to write:
"HELLO ".chomp(" ").downcase
=> hello
And:
"HELLO ".chomp!(" ").downcase
=> hello

Resources