How to change "-0"s to "0" in a string?

My problem

In my script, there are vector3’s as strings. Sometimes, zeroes will be negative due to floating points. When comparing the strings later on they will falsely say they aren’t equal because one string has a non-negative 0 and the other has a negative 0, even though they are technically equal.


How would I change the “-0”s to “0” in a string?

So for example, this:

local stringBad = "0.25, -0.5, -0"

would become this:

local stringGood = "0.25, -0.5, 0"
3 Likes

i know my solution is a bit weird, but you could try to use the math.abs() function to make the -0 become 0

4 Likes

Well, for my use it’d be nice to be able to use string manipulation to fix it. I’m going to leave the solution unchecked for a bit and if there isn’t a string based solution I’ll go this route. Thank you!

1 Like

Could you do string.gsub(str, "-0$", "0")?

3 Likes

Getting there, although this only applies for the last number

1 Like
string.gsub(str, "^-0$", "0")
1 Like

I thought that’s what you wanted? In your example it only shows the last number being changed.

There’s no elegant way of doing it once the string has already been created, doing a check for any components that’re equal to negative zero and replacing them while the vector is still in numerical form is probably your best bet.
You can make a faulty pattern match for it. With how lua matches work, you can’t create one single match that will replace all 3 coordinates at once (and still properly account for things such as -0.1234).

If you’re willing to use 2 gsubs at once though:

-- first & second -0s     final -0
str:gsub('%-0,', '0,'):gsub('%-0$', '0')

“-” is a special reserve character for string patterns in lua, so we need to escape it and turn it into a literal character by adding a % symbol in the string when we use string.gsub, this code example should do the trick for you. :slight_smile:

local a = "50, -0, -0"

a = string.gsub(a, "%-0", "0")

print(a)
function fixNum(x: number): number 
	--Lua thinks -0 == 0
	return x == 0 and 0 or x
end

function fixString(x: string): string
	return x == "-0" and "0" or x
end

function fixVector2(v: Vector2): Vector2
	return Vector2.new(fixNum(v.X), fixNum(v.Y))
end

function fixVector3(v: Vector3): Vector3
	return Vector3.new(fixNum(v.X), fixNum(v.Y), fixNum(v.Z))
end

--old solution
function fixVectorStringOld(vs: string): string
	local coords = {}
	for _, coord in pairs(vs:split(", ")) do
		table.insert(coords, fixString(coord))
	end
	return table.concat(coords, ", ")
end

--new solution
function fixVectorString(vs: string): string
	return ({(vs..","):gsub("-0,", "0,")})[1]:sub(0, -2)
end

print(fixVectorString("0.5, -0, -2")) --0.5, 0, -2
2 Likes

your fixVectorString() falsely replaces negative decimals with their positive ones (as of writing)

local function fixVectorString(vs: string): string
	--two lined on purpose, don't change this
	local s = vs:gsub("%-0", "0")
	return s
end

print(fixVectorString("-0.5, -0, -2")) -- returns "0.5, 0, -2", should return "-0.5, 0, -2"

Also it doesn’t need to be two-lined, you could’ve just done return vs:gsub("%-0", "0") and it wouldn’t have changed the output

2 Likes

Reverted my method to the old one, if anyone finds a pattern that doesn’t mess with cases like -0.5 tell me.

Also, it changes the output, gsub returns a tuple. Basically, instead of returning string as the fixVectorString function denotes, it would return string, number where the second number argument within that tuple is the number of substitutions made.

You can test this in the following way:

local noSpaces, substitutions = string.gsub("foo bar", " ", "")
print(noSpaces, substitutions) --foobar, 1

The reason you didn’t notice it was because you stored the function result in a variable, which ignores the second tuple argument(basically you did what I did above but outside the function).

1 Like

You are correct, I noticed that it was returning the number of substitutions but didn’t think enough of it to realise that it could have an impact.
Maybe a different comment or usage of the select function would have made this clearer though.

local stringBad = "0.25, -0.5, -0"

local temp = {}
local temp2 = stringBad:gsub(" ",""):split(",")

for i = 1, 3 do
    local n = tostring(temp2[i]):match("%d+%.?%d?%d?%d?")
    if tonumber(n) == 0 and tostring(temp2[i]):match("%-") then
        temp[i] = math.abs(tonumber(temp2[i]))
    else
        temp[i] = temp2[i]
    end
end

local stringGood = table.concat(temp, ", ")

Simple and ez solution

1 Like

There were a ton of interesting solutions but I think this is the best one. Thank you and everyone who helped!

Don’t want to micromanage here, but like I said in my response, you can’t fix a vector string with only one :gsub, you’ll need one to match the final component.

local function fixVectorString(vs: string): string
	--two lined on purpose, don't change this
	--comma included on purpose so it doesn't trigger for cases like -0.5
	local s = vs:gsub("-0,", "0,")
	return s
end

print(fixVectorString("-0, -0, -0")) -- prints "0, 0, -0"

Also, it’s good practice to declare your functions local, unless you want to use it in a global context.

Give me an example in which the above function won’t work.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.