Related to an old PHP question here:
How to increment numeric part of a string by one?
I can have any random format of strings (invoice numbers) like this:
I would like to take these as an input and output:
The last numeric portion is incremented by one. In the case where there is no match it returns "" OR just adds a "-1" to the end. I can handle the logic on my end if its easier to just return "".
On the ruby / rails side it would seem to be like this:
"foo".gsub(/(o+)/, '\1\1\1')
#=> "foooooo"
I just can't seem to figure out the regex logic.
You are looking for the method String#succ.
def increment_numeric(str)
str.gsub(/\d+/) { |s| s.succ }
end
increment_numeric "INV-005" #=> "INV-006"
increment_numeric "INV-5" #=> "INV-6"
increment_numeric "5567" #=> "5568"
increment_numeric "5567-1A" #=> "5568-2A"
increment_numeric "567-001A" #=> "568-002A"
increment_numeric "INVOICE" #=> "INVOICE"
The operative line can be written more succinctly as follows:
str.gsub(/\d+/, &:succ)
In view of the asker's comment below, the following might be more appropriate.
R = /\d+\z|(?<=\d)\p{L}\z/
def increment_numeric(str)
str.sub(R) { |s| s.succ }
end
increment_numeric "INV-005" #=> "INV-006"
increment_numeric "INV-5" #=> "INV-6"
increment_numeric "5567" #=> "5568"
increment_numeric "5567-01" #=> "5567-02"
increment_numeric "5567-1A" #=> "5567-1B"
increment_numeric "567-001A" #=> "567-001B"
increment_numeric "A567-001B" #=> "A567-001C"
increment_numeric "INVOICE-1" #=> "INVOICE-2"
increment_numeric "INVOICE" #=> "INVOICE"
The regular expression reads can be written in free spacing mode to make it self-documenting:
R =
/
\d+\z # match one or more digits at the end of the string
| # or
(?<=\d) # assert that the following match is preceded by a digit
\p{L}\z # match one Unicode letter at the end of the string
/x # invoke free-spacing regex definition mode
(?<=\d)
is a positive lookbehind.
There is at most one substitution so either gsub
or sub
can be used.