Search code examples
lualua-table

Reference Dynamic multidimensional table


I have a data structure of nested tables that can be N deep. for example

local data = {
  [1] = {
     [1] = { "stuff" },
  [2] = {
     [1] = { "stuff" },
     [2] = { "more stuff" },  
     [3] = {
        [1] = "deeper stuff"
     }
  }
}

Now I can reference "deeper stuff" via data[2][3][1] But is there a way that I can store the 2-3-1 as a key so I can reference this data[key] ?

I am storing a set of actions that are transformed and looped over in a denormalised table. I want to be able to reference that this particular action came from a specific point in the original data table. As this is n levels deep is there a dynamic way of writing [2][3][1]...[n]?


Solution

  • You cannot have a single multi-dimensional key. The only way to achieve something like that would be to have some string like "2-3-1" which you then use in an __index metamethod that tranlates it to the separate keys.

    setmetatable(data, {
      __index = function(t, k)
        for index in k:gmatch("%d+") do
            -- insert fancier error handling here
            if not t then error("attempt to index nil") end
            t = rawget(t, tonumber(index))
        end
        return t
      end
      })
    
      print(data["2-3-1"]
    

    Alternatively you use a table as key

    setmetatable(data, {
      __index = function(t, k)
        for i,v in ipairs(k) do
          if not t then error("attempt to index nil") end
          t = rawget(t, v)
        end
        return t
      end
      })
    
      print(data[{2,3,1}]
    

    There are of course more ways to implement the table access in __index.

    If data is global you could also use load and do something like

    local k = string.gsub("1-1-1", "(%d+)%-?", "[%1]")
    local element = load("return data .. k")()
    

    But please don't make data global just so you can do this.

    Or you write a function that does that without using metamethods as Egor suggested...