Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Fuzzy finding in accessors?

How could I tell if a typo has been made in some code when accessing an object member, and determine the member they meant to access?

As an example,

local PlsNoTypos = {
	Method = function(self)
		self.Health = 3;
	end;
	Health = 6;
	Car = true;
};

local CantFindIt = PlsNoTypos.BananaCar --> nil, because it's not close enough to any object members
local Methd = PlsNoTypos.Methd --> PlsNoTypos.Method and a warning because it's a typo for Method
local Car = PlsNoTypos.Cat --> true and a warning, because it's a typo for Car (Because Cat doesn't exist)

How would I make that happen in a table? How could I also make the table return nil and a warning if two or more members are sufficiently close/similar to the key in question?

1 answer

✓ Accepted answer

Maybe not the best idea to guess

First: If the programmer made a mistake, you probably should not give them what you think they want. It could easily lead to very confusing / very dangerous code.

Imagine someone makes a typo in code going into a DataStore, and this thing makes the wrong guess. Sure, it may have printed a warning, but it also destroyed information that you won't be able to recover.

-- e.g.,
save( weoifjasodifjasodij )
-- ^ If it's not right, the script does NOT know what the user meant, period
-- It would be wrong to ever try to guess what was meant by the above
-- It's not less wrong if they're off by anymore more than a capitalization error.

You should probably error when there is a problem.


The solution: Edit distance

A simple solution is to measure the edit distance between the requested key and all the methods. Edit distance is basically the 'number of mistakes' you would need to make to get between two strings.

A common algorithm to compute this uses Levenshtein distance. This makes one "edit" be a substitution (cat --> bat), insertion (bat --> brat) or deletion (brat --> bat).

From Rosetta Code, here is an implementation:

function leven(s,t)
    if s == '' then return t:len() end
    if t == '' then return s:len() end
 
    local s1 = s:sub(2, -1)
    local t1 = t:sub(2, -1)
 
    if s:sub(0, 1) == t:sub(0, 1) then
        return leven(s1, t1)
    end
 
    return 1 + math.min(
        leven(s1, t1),
        leven(s,  t1),
        leven(s1, t )
      )
end
 

I believe it can be made faster, though.

Another option for edit distance could use the longest common subsequence algorithm. You might also want to adjust these algorithms to account for mistakes in capitalization, etc.

Then just compare the edit distances to all members.

function guessMember(object, name)
	local keys = {}
	for key in pairs(object) do
		table.insert(keys, key)
	end
	table.sort(keys, function(a, b)
		return leven(name, a) < leven(name, b)
	end)
	if leven(keys[1], name) <= 3 then
		-- up to 3 typos...
		if leven(keys[2], name) > leven(keys[1], name) then
			-- no ties in closest words
			return keys[1]
		end
	end
end

There are faster ways to build these sort of "spell check" systems, but they get more complicated. Since most objects that you need this for shouldn't have many fields on it (and in a correct run of the program this code never runs anyway) they're not needed here.

6 comments

0

Wonderful. I don't think your thing about DataStores makes any sense, perhaps particularly not in the context of a DataStore - The only similar functions are (Get/Set)Async, which have a distance of 1 and therefore are too similar for the thing to pick one or the other.

0

That said, you have the same solution as I had, and I'm satisfied.

0

What I meant was code being used to figure out WHERE/WHAT is put into a datastore

0

The human might have expected a method to behave differently than they thought or thought a method exists that didn't. It's dangerous to execute things the human didn't ask for.

0

And that is why I said it would give a warning every time the fuzzy match was used. I do understand what I'm doing, and I do understand the implications.

0

"I just broke all your data" is not a very helpful warning.

This discussion is preserved as it was. New answers, comments, and votes are closed.