AreTablesSame + DeepCopy Modules

Today, I give you two simple but helpful resources from my library:
AreTablesSame - Does a seep search to check if two tables are equal
DeepCopy - Creates a copy of a given table and all of its sub-tables

Simply checking if two tables are equal using the == operator does not work. Comparing tables does not compare the contents of the tables, only the memory addresses. Thus, we have AreTablesSame for this case. For example, we don’t need to make a data store set request if our save data matches the data we had upon joining the game. We would need a special utility to check the two tables.

function KeysMatch(table1, table2, key)
	if table2[key] == nil then
		return
	end

	if typeof(table1[key]) ~= typeof(table2[key]) then
		return
	end

	if typeof(table1[key]) == "table" then
		if not CheckKeys(table1[key], table2[key]) then
			return
		end
	elseif table1[key] ~= table2[key] then
		return
	end

	return true
end

function CheckKeys(table1, table2)
	if not table1 then
		return
	end

	for key in pairs(table1) do
		if not KeysMatch(table1, table2, key) then
			return
		end
	end

	return true
end

function AreTablesSame(table1, table2)
	if not CheckKeys(table1, table2) then
		return
	end

	if not CheckKeys(table2, table1) then
		return
	end

	return true
end

return AreTablesSame

For copying tables, we can usually rely on table.clone. However, if we have embedded tables, they will not be copied using this method. We need a utility to recursively copy the table’s contents. Therefore, we do it manually.

function Copy(original, copy)
	for key, value in pairs(original) do
		if typeof(value) == "table" then
			copy[key] = {}
			Copy(original[key], copy[key])
		else
			copy[key] = value
		end
	end
end

return function(original)
	local copy = {}
	
	Copy(original, copy)
	
	return copy
end

Hope that helps anybody looking for a quick solution to these problems. :slight_smile:

7 Likes

Here’s my version of this:

--[[ READ ME: 
Use of this library requires that dictionaries you pass into it do not have two of the same keys. No two keys can be the same.
]]
local DictionaryUtils = {}

-- Deeply copies a dictionary.
function DictionaryUtils.DeepCopy(dictionaryToCopy) 
	if type(dictionaryToCopy) ~= "table" then return dictionaryToCopy end
	local deepCopiedTable = {} 
	
	for index, value in pairs(dictionaryToCopy) do 
		if type(value) == "table" then
			value = DictionaryUtils.DeepCopy(value)
		end
		deepCopiedTable[index] = value
	end
	
	return deepCopiedTable
end

function DictionaryUtils.DeepPrint(dictionaryToCopy)
	if type(dictionaryToCopy) ~= "table" then return dictionaryToCopy end
	local deepCopiedTable = {} 

	for index, value in pairs(dictionaryToCopy) do 
		print(tostring(index), tostring(value))
		if type(value) == "table" then
			value = DictionaryUtils.DeepCopy(value)
		end
		deepCopiedTable[index] = value
	end

	return deepCopiedTable
end

-- Copies a dictionary.
function DictionaryUtils.ShallowCopy(dictionaryToCopy) 
	if type(dictionaryToCopy) ~= "table" then return dictionaryToCopy end 
	local shallowCopiedTable = {} 
	
	for index, value in pairs(dictionaryToCopy) do 
		shallowCopiedTable[index] = value
	end
	
	return shallowCopiedTable
end

-- Checks a dictionary to see if a specific key is in the dictionary
function DictionaryUtils.IsKeyInDictionary(Dictionary, Key)
	for index, value in pairs(Dictionary) do 
		if index == Key then
			return true 
		end
		
		if type(value) == "table" then 
			local result = DictionaryUtils.IsKeyInDictionary(value, Key)
			if result then return true end
		end
	end
end

-- Sets a pre-existing key in a dictionary to a new value deeply.
function DictionaryUtils.DeepSet(dictionaryToSet, keyToSet, valueToSet) 
	if type(dictionaryToSet) ~= "table" then return dictionaryToSet end
	
	if not DictionaryUtils.IsKeyInDictionary(dictionaryToSet, keyToSet) then 
		local dictionaryToSet = DictionaryUtils.DeepCopy(dictionaryToSet) 
		dictionaryToSet[keyToSet] = valueToSet 
		return dictionaryToSet
	end
	
	local deepCopiedTable = {} 
	local foundKey = false
	
	for index, value in pairs(dictionaryToSet) do 
		if type(value) == "table" then
			value = DictionaryUtils.DeepSet(value, keyToSet, valueToSet)
		end
		
		if index == keyToSet then
			deepCopiedTable[keyToSet] = valueToSet -- Overwrites the same key with a new value.
			foundKey = true
		else
			deepCopiedTable[index] = value
		end
	end
		
	return deepCopiedTable
end

-- Gets the value of a key.
function DictionaryUtils.DeepGetValueOfKey(dictionary, keyToGet) 
	for index, value in pairs(dictionary) do
		if index == keyToGet then
			return value
		end
		
		if type(value) == "table" then 
			local result = DictionaryUtils.DeepGetValueOfKey(value, keyToGet)
			if result then
				return result
			end
		end
	end
end

-- Takes a function with the parameters (index, value) and runs it deeply on every key value pair.
function DictionaryUtils.DeepFunction(dictionary, functionToRun, cleaning : boolean)
	for index, value in pairs(dictionary) do 
		if type(value) == "table" then
			DictionaryUtils.DeepFunction(value, functionToRun)
		end
		
		functionToRun(index, value)
	end
end

-- Checks a dictionary to see if a key-value pair is existant in the dictionary.
function DictionaryUtils.IsPairInDictionary(dictionary, keyToCheck, valueOfKey)
	for index, value in pairs(dictionary) do 
		if index == keyToCheck and value == valueOfKey then 
			return true
		end

		if type(value) == "table" then 
			local result = DictionaryUtils.IsPairInDictionary(value, keyToCheck, valueOfKey)
			if result then return true end
		end
	end
	
	return false
end

-- Compares two dictionary deeply to see if they are the same.
function DictionaryUtils.CheckIfSame(d1, d2)	
	if type(d1) ~= "table" or type(d2) ~= "table" then return false end	
	
	for index, value in pairs(d1) do 
		if not DictionaryUtils.IsPairInDictionary(d2, index, value) then return false end
	end
	
	for index, value in pairs(d2) do 
		if not DictionaryUtils.IsPairInDictionary(d1, index, value) then return false end
	end

	return true
end

-- Returns the amount of key-value pairs in a dictionary.
function DictionaryUtils.ShallowDicToNum(dictionary : any) 
	local counter = 0
	
	for index, value in pairs(dictionary) do 
		counter+=1
	end
	
	return counter
end

return DictionaryUtils
2 Likes