How to replace the last instance of a string?

I am not really sure how to explain this. what I’m trying to achieve is I want to replace the last word of a string with something else.

currently I’ve tried:

input.Text = string.gsub(input.Text, ".$", String, 1)
--this only replaces the last  character, not the entire last word of the string.
input.Text = string.gsub(input.Text, Split[#Split], String, 1)
--this replaces everything that is equal to the last word of Split, for example it would replace all the k's in "kitten kitten" and cause some weird broken string.

Does anyone know how I can go about replacing the last word of a string and only that one. the word may be a single character or a full word like “kitten” for example?

(if you could combine the .$ and the Split[#Split] it would do what I want it to do, it would replace the last word of the string and only that word, no other match of it.)

I dont know much about the string patterns but…

local str = "Hello World?"
local newstr = string.sub(str,1,#str-1).."!"
print(newstr,str) -- output: Hello World!, Hello World?

that is not it. it needs to place the entire last part of the string, so It should output: Hello!

Oh so you mean word? right?

yeah for example if we have the string

local str = "Kitten k"
--we want to replace k with Yes
--output: Kitten Yes

Another example:

local str = "Kitten kelp"
--we want to replace kelp with Yes
--output: Kitten Yes

the problem with Gsub is if you try to replace the last character, it doesn’t replace the last character it replaces all instances of the last character like this:

local str = "kitten Kitten Kitten K"
print(str:gsub(".$", "Yes", 1)) -->  Yesitten Yesitten Yesiten Yes

as far as I know “.$” is the only way to select the last character.

What you can do is split the string, using any separator string you’d like, such as a space so split the string from every space and make it into a table,

Then with that table, change the last element to whatever you want and then concat it back

local str = "Hello there world!"
local splits = str:split(" ")

splits[#splits] = "mars!"

str = table.concat(splits, " ")

Printing str before and after gave this,

image

Not sure if there’s a more efficient way, this is what my brain thought of at first

1 Like

You can use [%w_]+ which means “alphanumeric chars or underscores”.

Then invert the group to match everything after that so you keep trailing spaces for example.

Something like:

local function ReplaceLastWord(str, with)
  return string.gsub(str, "[%w_]+([^%w_]*)$", with .. "%1")
end

-- testing

local function is(str, with, expected)
  local actual = ReplaceLastWord(str, with)
  print("(" .. str .. ", " .. with .. ")", "-> " .. actual)
  assert(actual == expected, "expected " .. expected)
end

is("hello", "world", "world")
is("test test", "yes", "test yes")
is(" with spaces.  ", "keeps", " with keeps.  ")
is("with under_scores_", "works", "with works")
3 Likes

tysm. this worked much appreciated. i gotta practice string patterns more.

another thing you can do

local str = "Kitten kelp"
local newstr = str:gsub(str:split(" ")[#str:split(" ")],"Yes")
-- output: Kitten Yes (i think didnt tried it)
local function ReplaceLastWord(str, rep)
	return string.gsub(str, "%S+$", rep)
end

print(ReplaceLastWord("Hello world!", "mars!")) --Hello mars!
print(ReplaceLastWord("foo bar", "foo")) --foo foo

Here’s a benchmark test between the two solutions.

local function ReplaceLastWord(str, rep)
	return string.gsub(str, "%S+$", rep)
end

local function ReplaceLastWord2(str, with)
	return string.gsub(str, "[%w_]+([^%w_]*)$", with .. "%1")
end

local T1 = os.clock()
for _ = 1, 10000 do
	ReplaceLastWord("Hello world!", "mars!")
end
local T2 = os.clock()

local T3 = os.clock()
for _ = 1, 10000 do
	ReplaceLastWord2("Hello world!", "mars!")
end
local T4 = os.clock()

print(T2 - T1)
print(T4 - T3)

image

I appreciate a good benchmark :slight_smile:

But your method does not pass the same test cases as mine does. For instance, yours does not handle trailing white space and includes punctuation as part of the final word.

That’s not to say yours is not better—depending on OP’s use case it could work—just that you can’t directly compare them because they don’t have the same functionality.

1 Like

Adding %P to the function I provided produces the same benchmark result, trailing spaces/trailing consecutive punctuation characters are niche enough that they can be ignored.

1 Like

curious though, it does not appear to remove a whitespace counting as the last word though. is there a variant of this that removes the last character?

I do not consider white space nor punctuation to be a word, do you?

If you’re asking a different question or clarifying your original one, it would help if you defined exactly what you mean when you say “word”. Give a dozen examples of inputs and expected output.

well your original solution was what i needed but I was also wondering if you had a formula for removing the last part of a string, may it be a word, a whitespace or a period, for example:

--removing the last part:
"Kitten " --> Kitten (there was a whitespace)
"Kittens are cool!" --> Kittens are cool
"Kittens are cool" --> Kittens are 

You could combine these patterns alongside what others have already suggested you here.

I dont think there is an existing ‘formula’. You’d need to use these + these: