RemoveCyclic - Remove cyclic references in tables

I only made this because of a post I came across a post of someone trying to send a table that has a cyclic reference over a bindable function. Attempting this will throw an error. So I made a simple module to remove cyclic references in tables. I don’t know if this problem is common, I don’t know if anyone cares, but I made the module so I thought I may as well post it here.

While this may not be the most efficient method, it does work on all types of tables, arrays, dictionaries and mixed tables. I have tested it with a depth of 4 with no issues but I cannot say if it’ll work at every depth.

The usage is simple:

local removeCyclic = require((...).RemoveCyclic).removeCyclic
local t1 = removeCyclic(t)

When using this module, the original table is slightly altered, but still has cyclic references which makes it unusable. If you’re wondering why I don’t deep copy it, well it’s pretty obvious, the cyclic references would cause a deep copy to infinitely run. For this reason I recommend clearing the original table from memory after using this utility.

As stated, this works on all types of tables including mixed tables, arrays within dictionaries and dictionaries within arrays.

Here’s the module:

And if you’re into the nerdy technical stuff, here’s the source:

Nerdy technical stuff
local module = {}

function removeCyclic(t, p)
	local t1 = {}
	for k in pairs(t) do
		if table.find(p, t[k]) then 
			continue 
		end
		if typeof(t[k]) ~= "table" then
			t1[k] = t[k]
		else
			table.insert(p, t[k])
			t1[k] = t[k]
			local nonCyc = removeCyclic(t[k], p)
			for k1 in pairs(t1[k]) do
				if not nonCyc[k1] then
					t1[k][k1] = nil
				end
			end
			if getTableType(t1[k]) == "Array" then
				removeNilFromTable(t1[k])
			end
		end
	end
	return t1
end

function removeNilFromTable(t)
	local prev = 0
	for i, v in pairs(t) do
		if typeof(v) == "table" and getTableType(v) == "Array" then
			removeNilFromTable(v)
		end
		if prev+1 ~= i then
			table.remove(t, prev+1)
		end
		prev = i
	end
end

--[[
	Source: https://devforum.roblox.com/t/detecting-type-of-table-empty-array-dictionary-mixedtable/292323/15
	@XAXA
]]
function getTableType(t)
	if next(t) == nil then return "Empty" end
	local isArray = true
	local isDictionary = true
	for k, _ in next, t do
		if typeof(k) == "number" and k%1 == 0 and k > 0 then
			isDictionary = false
		else
			isArray = false
		end
	end
	if isArray then
		return "Array"
	elseif isDictionary then
		return "Dictionary"
	else
		return "Mixed"
	end
end

function module.removeCyclic(t)
	local nonCyclic = removeCyclic(t, {t})
	if getTableType(nonCyclic) == "Array" then
		removeNilFromTable(nonCyclic)
	end
	return nonCyclic
end

return module

As I said, maybe not the best, or the fastest, but a method that works on all types of tables.

9 Likes