Page 1 of 1

How to track table delta (solved?)

Posted: Sat Feb 16, 2013 8:20 am
by Taehl
In the process of writing netcode, I realized how incredibly useful it would be if I could somehow know which values in a table have changed. Ideally these would be possible without me having to keep a whole copy of the table and constantly comparing. I thought I could do this with a metatable, however, the __newindex metamethod only gets called when assigning a value to a key which was previously empty - not whenever a value is updated.

Here was my attempt, however, it doesn't work right. It only finds when new values are added to the table, not when any value is updated.

Code: Select all

function deltaTable(watch, delta)
	setmetatable( watch, {
		__newindex = function(t,k,v)
			if v ~= rawget(t,k) then
				rawset(delta,k,v)
			end
			rawset(t,k,v)
		end
	} )
end
Any suggestions?

Re: How to track table delta?

Posted: Sat Feb 16, 2013 9:48 am
by Robin
Use a proxy table: the proxy remains empty and it only sets values in the other table.

You also need an __index for the proxy that takes values from that other table.

Re: How to track table delta?

Posted: Sun Feb 17, 2013 6:48 am
by Taehl
That's a good idea, Robin. Thanks. I ran with it in what's probably not the direction you had in mind, but what do you think of this?

Code: Select all

-- only downside: won't work with pairs()
function makeDeltaTable(t)
	t = t or {}
	t.__delta = {}
	t.__index = function(t,k) return getmetatable(t)[k] end
	t.__newindex = function(t,k,v)
		if v ~= getmetatable(t)[k] then getmetatable(t).__delta[k] = v end
		getmetatable(t)[k] = v
	end
	t.__unm = function(t) return getmetatable(t).__delta end	-- -table returns full delta table
	t.__sub = function(t,k) return getmetatable(t).__delta[k] end -- table-key returns delta value
	return setmetatable( {}, t )
end

-- here are some tests:
t = makeDeltaTable( {x=10,y=10} )
t.x,t.y = 10,25		-- only t.y changed
print("delta t.x = "..tostring(t-"x"))	-- nil, because x didn't change
for k,v in pairs(-t) do print("delta t."..k.." = "..v) end	-- only shows delta t.y = 25
t.__delta = {}						-- empty the delta table
print("t.__delta={}. Now delta t.y = "..tostring(t-"y"))	-- nil
print("t.x, t.y = "..t.x..", "..t.y)	-- 10, 25

Re: How to track table delta (solved?)

Posted: Sun Feb 17, 2013 9:51 am
by Robin
What I think of it depends on whether it works for you. If it does, great!

Re: How to track table delta (solved?)

Posted: Sun Feb 17, 2013 5:17 pm
by spectralcanine
You should probably make __delta private, but whatever fits your needs.

As to pairs, you can create your own iterator :)

Re: How to track table delta?

Posted: Sun Feb 17, 2013 7:21 pm
by Xgoff
Taehl wrote:That's a good idea, Robin. Thanks. I ran with it in what's probably not the direction you had in mind, but what do you think of this?

Code: Select all

-- only downside: won't work with pairs()
function makeDeltaTable(t)
	t = t or {}
	t.__delta = {}
	t.__index = function(t,k) return getmetatable(t)[k] end
	t.__newindex = function(t,k,v)
		if v ~= getmetatable(t)[k] then getmetatable(t).__delta[k] = v end
		getmetatable(t)[k] = v
	end
	t.__unm = function(t) return getmetatable(t).__delta end	-- -table returns full delta table
	t.__sub = function(t,k) return getmetatable(t).__delta[k] end -- table-key returns delta value
	return setmetatable( {}, t )
end

-- here are some tests:
t = makeDeltaTable( {x=10,y=10} )
t.x,t.y = 10,25		-- only t.y changed
print("delta t.x = "..tostring(t-"x"))	-- nil, because x didn't change
for k,v in pairs(-t) do print("delta t."..k.." = "..v) end	-- only shows delta t.y = 25
t.__delta = {}						-- empty the delta table
print("t.__delta={}. Now delta t.y = "..tostring(t-"y"))	-- nil
print("t.x, t.y = "..t.x..", "..t.y)	-- 10, 25
that could be prematurely optimized somewhat

Code: Select all

-- only downside: won't work with pairs()
function makeDeltaTable(t)
   t = t or {}
   t.__delta = {}
   t.__index = t
   t.__newindex = function(_,k,v)
      if v ~= t[k] then t.__delta[k] = v end
      t[k] = v
   end
   t.__unm = function(_) return t.__delta end   -- -table returns full delta table
   t.__sub = function(_,k) return t.__delta[k] end -- table-key returns delta value
   return setmetatable( {}, t )
end
you could even go so far as making the delta table a local, in which case it'd automatically become private simply by being an upvalue of some of the metamethods (it'd also be slightly faster too)