Search code examples
luametatablemeta-method

Is there a way to know if there is a key added or removed from an array in lua?


local t = {}
local mt = setmetatable({
        -- some meta method to know when a key is added or lost and prints a message
      }, t)

Is there a way of doing this. I talked about this with someone and they said i couldn't just do it with meta methods but proxies as well. I'm a bit stumped on how to make this work. Can anyone help?

Thanks


Solution

  • To track table keys in lua there are 2 most importaint keys in metatable: __index and __newindex.

    __newindex is used to create new key in the table if such key is not found. __index is used to get the value if there is no such key in table.

    With __newindex it is possible to track creation, but not assignment, so it is not possible to track key removal:

    <script src="https://github.com/fengari-lua/fengari-web/releases/download/v0.1.4/fengari-web.js"></script>
    <script type="application/lua">
    
    local t={}
    setmetatable(t, {
      __newindex = function(self, key, value)
        print('Added Key:'..key,'Value:'..value)
        rawset(self, key, value)
      end
    })
    
    t.test = 'test'
    t.test = nil -- delete not tracked
    t.test = 'test2'
    
    </script>

    Using proxy table and __newindex with __index we can track every assignment:

    <script src="https://github.com/fengari-lua/fengari-web/releases/download/v0.1.4/fengari-web.js"></script>
    <script type="application/lua">
    
    local t={}
    local proxytable={}
    setmetatable(t, {
      __newindex = function(self, key, value)
        if proxytable[key] then
          if value == nil then
            print('Deleted Key:'..key)
          else
            print('Changed Key:'..key,'Value:'..value)
          end
        else
          print('Added Key:'..key,'Value:'..value)
        end
        rawset(proxytable, key, value)
      end,
      __index = proxytable
    })
    
    t.test = 'test'
    t.test = nil
    t.test = 'test2'
    t.test = 'test3'
    t.test = nil
    
    </script>

    If you want enumerate table keys with pairs(), ipairs(), then you need to use metakeys __pairs and __ipairs as original table is allways empty.