0

I am working on a string incrementer project on codewars. Basicly, Writing a function which increments a string, to create a new string.

If the string already ends with a number, the number should be incremented by 1. If the string does not end with a number. the number 1 should be appended to the new string. If the number has leading zeros the amount of digits should be considered. foo -> foo1

foo001 ->foo002

foobar23 -> foobar24

foo099 -> foo100

My code is : input.gsub(/\d/,"")+input.split().map {|x| x[/\d+/].next!}.join(" ") https://repl.it/@tanilserbes/ViolentNoteworthyDowngrade . It works on this playground However it doesnt work on codewars. I get this error:

main.rb:5:in block in increment_string': undefined methodnext!' for nil:NilClass (NoMethodError)` Any idea?
thanks in advance!.

6
  • I get the same NoMethodError with an input of "foo" Commented Feb 24, 2020 at 2:07
  • 1
    Also, you might want to read up on what String#succ does. Commented Feb 24, 2020 at 2:12
  • "If the number has leading zeros the amount of digits should be considered" So what happens for "foo99", without a leading 0? Commented Feb 24, 2020 at 3:58
  • and I feel like String#succ would probably be less than beneficial for this one, because it'll increment "foo" to "fop" and "foo9" to "fop0"... just increases the number of checks you need to do, imo...though string.sub!(/(\d+)\Z/, &:next) || string << "1" should work well enough Commented Feb 24, 2020 at 4:08
  • You're getting "undefined methodnext!' for nil:NilClass". 99.9% of the time this means you have a misspelled variable or class name. Please see "How to Ask", "Stack Overflow question checklist" and "MCVE" and all their linked pages. Commented Feb 24, 2020 at 4:29

2 Answers 2

2

To see what's going on here, it's beneficial to run each of your commands individual and see what the output is to track down the error:

input = "foo"
input.gsub(/\d/, "") # => "foo"

So the left hand side of the + operator is going to become "foo" and now we need to see what the right hand side of the is:

input.split # => ["foo"]
["foo"].map { |x| x[/\d+/].next! }

As seen in the question, this is where the error happens, so let's dig into the code inside the map block, where the error is:

["foo"].map { |x| p x }
# Outputs: "foo"

So, x == "foo" at this point:

["foo"].map { |x| p x[/\d+/] }
# Outputs: nil

Since the string "foo" doesn't have any digits in it, the regex pulling out the digits from it, to increment them returns nil and then, without any safeguarding you increment that. NilClass doesn't have a method next!, so you get your error.

If the string was instead "foo1", though, you'd get:

["foo1"].map { |x| p x[/\d+/] }
# Outputs: "1"

Which returns the matched string, and then allows you to call next! (which is a synonym of the String#succ! method called out in the comments) on it. The reason it works in the playground is because the string has digits in it, and doesn't take into account or test the case where strings don't (the first example in the text of the question where "foo" should become "foo1").

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you ! You and Carry (the comment below) guys explained everything from the beginning to end. Much appreciated! How come i missed this error ?! Thanks again.
1

Here are some points to consider in developing your solution.

If your string were:

str = "ca9t00456"

the desired return value would be:

"ca9t00457"

(Note that the OP's solution would return an incorrect result ("cat10") for this string. The Codewars question does not say that the only digits in the string are those at the end; it only mentions the "number" at the end of the string".)

A reasonable first step would be to divide the string into two parts:

n = str.index(/\d+\z/)
  #=> 4
prefix = str[0, n]
  #=> "ca9t"
suffix = str[n..-1]
  #=> "00456"

See String#index. The regular expression, /\d+\z/, reads, "match one or more (+) digits (\d) followed by the end of the string (\z). The digit '9' is skipped over because it is neither followed by a digit nor is at the end of the string. See also See String#[].

The string we return will begin with (the value held by) prefix, so we can set that aside for now and concentrate on modifying suffix.

One approach would be:

((suffix.to_i) + 1).to_s
  #=> "457"

but then we would have to add the correct number of leading zeroes. Here that would be the same as the number of leading zeroes in suffix (2), but if suffix were, for example, 00999, it would be only one (01000). That could be done, but it's messy.

An easier way would be to use the method String#succ, as @steenslag suggested in the comments.

new_suffix = suffix.succ 
  #=> "00457"
"00999".succ
  #=> "01000"

Now we need only combine prefix and new_suffix.

Note what happens if we execute succ on the entire string:

"ca9t0456".succ
   #=> "ca9t0457" correct
"ca9t0999".succ
   #=> "ca9t1000" correct
"ca9t9999".succ
   #=> "ca9u0000" incorrect

As you see, there's a problem with the third example. That's why I chose to divide the string into two parts as a first step.

You need to investigate three other cases. The first is when the prefix is an empty string:

str = "00456"

the second is when the suffix is an empty string:

str = "ca9t"

and the third is when the string is empty:

str = ""

You can check if the previous calculations still work in the first case.

In the second case we would find:

n = str.index(/\d+\z/)
  #=> "cat9t".index(/\d+\z/) => nil

The nil value for n tells us that the desired return value is:

str + "1"
  #=> "ca9t" + "1" => "ca9t1"

Would that work?

1 Comment

Thanks Cary. I didn't even develop my solution, i solved it ! Thanks for showing step by step.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.