Good practice of making __index metamethod work with many table?

The code simply create a metatable with an index metamethod that simply allow you to use many table at once. Although working as intended I wonder if there any other good practice to achieve the same result. The only thing I hate is that I need to make another local function so that I can detect if there are the same key resulting to me separating the thread without delaying the return.

local Tables = { [1] = { b = 2 }, [2] = { b = "b" } }

local function CheckIfSame(Table, Index)
	local TableValue = Table[Index]

	for _, SubTable in ipairs(Tables) do
		for Key, Value in next, SubTable do
			if Key == Index and TableValue and TableValue ~= Value then
				Table["Replaced " .. Key] = Value
				SubTable[Key] = nil
			end
		end
	end
end

local Metatable = setmetatable({}, {
	__index = function(Table, Index)
		for _, SubTable in ipairs(Tables) do
			for Key, Value in next, SubTable do
				if Key == Index then
					rawset(Table, Key, Value)
					SubTable[Key] = nil

					CheckIfSame(Table, Index)

					return Value
				end
			end
		end
	end,
})

print(Metatable["b"])
print(Metatable["Replaced b"])

print(Metatable["b"])
print(Metatable["Replaced b"])

print(Tables)

Your code looks good for achieving the desired result of accessing multiple tables using a single metatable. However, there are a couple of suggestions to improve the implementation:

  1. Separate the checking of same keys into a separate function: You mentioned that you don’t like having to define another local function to check for the same keys. Instead of having the CheckIfSame function separate, you can define it within the metatable’s __index metamethod. This way, you can directly access the metatable and avoid the need for a separate function.
  2. Consider using a more descriptive name for the metatable: Instead of naming the metatable Metatable, you can use a more meaningful name that describes its purpose. This will make the code easier to understand.

Here’s an updated version of your code incorporating these suggestions:

local Tables = { [1] = { b = 2 }, [2] = { b = "b" } }

local Metatable = setmetatable({}, {
	__index = function(Table, Index)
		local function CheckIfSame(Table, Index)
			local TableValue = Table[Index]

			for _, SubTable in ipairs(Tables) do
				for Key, Value in next, SubTable do
					if Key == Index and TableValue and TableValue ~= Value then
						Table["Replaced " .. Key] = Value
						SubTable[Key] = nil
					end
				end
			end
		end

		for _, SubTable in ipairs(Tables) do
			for Key, Value in next, SubTable do
				if Key == Index then
					rawset(Table, Key, Value)
					SubTable[Key] = nil

					CheckIfSame(Table, Index)

					return Value
				end
			end
		end
	end,
})

print(Metatable["b"])
print(Metatable["Replaced b"])

print(Metatable["b"])
print(Metatable["Replaced b"])

print(Tables)

These updates make the code more concise and eliminate the need for a separate function. The functionality remains the same, allowing you to access multiple tables using a single metatable.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.