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.
save( weoifjasodifjasodij )
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
if leven(keys[2], name) > leven(keys[1], name) then
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
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.
That said, you have the same solution as I had, and I'm satisfied.
What I meant was code being used to figure out WHERE/WHAT is put into a datastore
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.
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.
"I just broke all your data" is not a very helpful warning.