Daniel
Daniel

Reputation: 3531

Recursive metamethod __index?

How can I extend my __index metamethod so that it works regardless of the level of nesting? My current approach works only for data.raw.a1.a2, but not for data.raw.b1.b2.b3 and following.

data = {
    raw = {}
}

setmetatable(data.raw, { __index = function(t,k)
    t[k] = {}
    return t[k]
end })

data.raw.a1.a2 = { foo = "bar" }
data.raw.b1.b2.b3 = { foo = "bar" } -- throws an error
data.raw.c1.c2.c3.c4 = { foo = "bar" } -- throws an error

I tried adding a second setmetatable but this only works for one more level of nesting.

data = {
    raw = {}
}

setmetatable(data.raw, { __index = function(t,k)
    t[k] = {}
    -- below is new
    setmetatable(t[k], { __index = function(t,k)
        t[k] = {}
        return t[k]
    end })
    -- above is new
    return t[k]
end })

data.raw.a1.a2 = { foo = "bar" }
data.raw.b1.b2.b3 = { foo = "bar" } -- now this works
data.raw.c1.c2.c3.c4 = { foo = "bar" } -- this still throws an error

I don't know how I can make this recursive, so that it works for all levels of nesting. Send help.

Upvotes: 2

Views: 120

Answers (1)

Oka
Oka

Reputation: 26385

Use a constructor function to create tables with a metatable that has an __index metamethod that recursively calls the constructor function to create new table depths when an indexed key is not present.

Here is a condensed example, but this can be written to reuse the metatable.

local function nestable(t)
    return setmetatable(t or {}, {
        __index = function (self, key)
            local new = nestable {}
            rawset(self, key, new)
            return new
        end
    })
end

local data = nestable {}

data.raw.a1.a2 = { foo = 'bar' }

print(data.raw.a1.a2.foo)

Result:

bar

Upvotes: 5

Related Questions