Is there any way to detect changes in a table?
Like let’s say I have table
tab = { name = “abc” , age = “10”}
I make changes like tab.name = “john” and everytime it change, i want it to print the new value, how it can be done?
You can use the override the __newindex
metamethod to track table changes
local t = {name = "abc", age = "10"}
local mt = {
__newindex = function(t, k, v)
warn("Updating table key "..tostring(k).." with value "..tostring(v))
rawset(t, k, v)
end
}
setmetatable(t, mt)
t.name = "bca"
-- Output should be "Updating table key name with value bca"
I’m pretty sure _newindex
only fires when a new key is added, not when a key is updated. To make it fire every time, you will have to make _newindex
prevent the value from being set in the table, i.e. proxy it to another table.
That makes sense to do if you don’t trust the person who’s using your table, but otherwise it’s overcomplicated.
@robloxjw02
Instead of trying to detect changes, use a setter function.
Do not set tab.name = "john"
.
Instead, set up a function so that you can SetName(tab, "john")
or tab:SetName("john")
.
tab = { name = “abc” , age = “10”}
function SetName(self, value)
print("Renaming " .. self.name .. " to " .. value)
self.name = value
end
SetName(tab, "john")
tab.SetName = SetName
tab.SetName(tab, "joel") -- uses dot
tab:SetName("jose") -- uses colon
If you have a lot of properties to change, or are finding yourself turning that function into Set(self, key, value)
, then you might consider the metamethod.
@/Syntheix was on the right track with using __newindex. To make it work whenever you override an existing property, you can use a proxy table assuming you know how metatables work. This approach wouldn’t require you to change any of your code aside from the constructor:
local object = {}
object.__index = object -- this will allow us to get properties from the object instead of returning nil
object.__newindex = function(self, i, v)
print('Setting',i,v)
object[i] = v -- we don't have to use rawset because `object` does not have a metatable, it *is* the metatable, therefore it won't invoke any of the later proxy table's metamethods
-- in this function, you can also handle any errors, such as if an invalid type is assigned to the property
end
object.name = 'abc'
object.age = 10
local myProxyTable = setmetatable({}, object) -- whenever you try to set a new value to the first table, it will pass this table to the __newindex metamethod which then will allow you to set the new values while still able to access its properties
myProxyTable.name = 'bob' -- prints "Setting name bob"
Although it’s important that when you want to set any of the object’s properties, you use myProxyTable
, not object
.
Also, it’s important to note that using a function for __newindex is bad for performance so I suggest using the function Eestlane said unless changing to using a function is so destructive to your workflow that you’re unable to do so in a reasonable amount of time.