Issues when attempting to call method from metatable

When attempting to call :ToArray() in my BinaryTree module from the Script listed below, I get “ServerScriptService.BinaryTree.Script:12: attempt to call missing method 'ToArray' of table” Why? I’m pretty sure it has something to do with me using a metatable, but it feels as if the method should work. The scripts are below:

BinaryTree module:

local BinaryTree = {}
BinaryTree.__index = BinaryTree

function BinaryTree:ToArray(): {Vector3}
	return {
		rawget(self, "Left"):ToArray(),
		[rawget(self, "Key")] = rawget(self, "Value"),
		rawget(self, "Right"):ToArray()
	}
end

function BinaryTree.new(key: number?, value: any?)
	local self = setmetatable({
		Key = key,
		Value = value,
		Left = nil,
		Right = nil
	}, BinaryTree)
	return self
end

function BinaryTree:__index(key: number): any?
	local currentKey = rawget(self, "Key")
	
	if not currentKey or key then
		return(nil)
	end
	
	if key < currentKey then
		return(rawget(self, "Left")[key])
	elseif key > currentKey then
		return(rawget(self, "Right")[key])
	else
		return(rawget(self, "Value"))
	end
end

function BinaryTree:__newindex(key: number, value: any?)
	local current = self
	local parent = self
	while current and rawget(current, "Key") do
		parent = current
		if key < rawget(current, "Key") then
			current = rawget(current, "Left") -- Move left if key is smaller than current's key
		elseif key > rawget(current, "Key") then
			current = rawget(current, "Right") -- Move right if key is larger than current's key
		else
			rawset(current, "Value", value) -- Overwrite value of key if it already exists.
			return
		end
	end
	if not rawget(parent, "Key") then
		rawset(parent, "Key", key)
		rawset(parent, "Value", value)
	elseif key < rawget(parent, "Key") then
		rawset(parent, "Left", BinaryTree.new(key, value))
	else
		rawset(parent, "Right", BinaryTree.new(key, value))
	end
end

function BinaryTree:__len()
	if not rawget(self, "Key") then
		return(0)
	end
	
	local left = rawget(self, "Left") or {}
	local right = rawget(self, "Right") or {}
	
	return(#(rawget(self, "Left") or {}) + 1 + #(rawget(self, "Right") or {})) -- Blessed be thy simple function
end

return BinaryTree

Script:

(parented to BinaryTree)

BinaryTree = require(script.Parent)

test1 = BinaryTree.new()

test1[1] = 2
test1[-1] = "key -1 is negative"
test1[6] = "h"
test1[4] = 0
test1[-1.824] = 0
test1[3623] = 0

print(test1:ToArray())

As you can see, it should be printing an array of all of the keys and values in the array, but it doesn’t even know :ToArray() exists! Why?

overriding .__index with a function so the metamethod :ToArray() doesn’t exist anymore

2 Likes

It’s because your __index function is overwriting your __index pointer to BinaryTree, so when the __index function is called (when you’re indexing your BinaryTree object for a key “ToArray”), it’s always returning rawget(self, "Value") because your if-statement is reaching the else branch.

You can get around this by checking that key == self then at the end return BinaryTree

function BinaryTree:__index(key: number): any?
	local currentKey = rawget(self, "Key")
	
	local numericKey = tonumber(key)
	
	if not currentKey or not key then
		return(nil)
	end
	
	if not numericKey then
		return BinaryTree[key]
	end
	
	if numericKey < currentKey then
		return(rawget(self, "Left")[key])
	elseif numericKey > currentKey then
		return(rawget(self, "Right")[key])
	else
		return(rawget(self, "Value"))
	end
end
2 Likes

I made those exact modifications and the error still persists. Same issue.

Oversight on my end, when you index test1 for ToArray, key will be a string, and strings can be compared using logical < > operators, so that’s why your < and > checks passed, try now, I made an edit.

This does give you a new error though when rawget(self, "Left") is nil, but that can be solved by checking that self.Left is truthy

1 Like

I actually decided to use a type check instead. It also seems to add the ability to request more information about the tree, like the key, value, left, and right of a node, which is something I want to expose. Here’s my modification:

function BinaryTree:__index(key: number): any?
	local currentKey = rawget(self, "Key")
	local numericKey = tonumber(key)

	if type(key) ~= "number" or (not currentKey or key) then
		return BinaryTree[key]
	end
	
	if numericKey < currentKey then
		return(rawget(self, "Left")[key])
	elseif numericKey > currentKey then
		return(rawget(self, "Right")[key])
	elseif numericKey == currentKey then
		return(rawget(self, "Value"))
	end
	
	return BinaryTree
end

Thanks for your help, @7z99!

1 Like

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