Why does `gsub!` return `nil`?

3.3k views Asked by At

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?

2

There are 2 answers

0
Yu Hao On

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.

0
Jacob Raihle On

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