Hello, everyone!
I’d like to share with you three useful functions that are specifically designed for working with tables and could help you with DataStore saving and loading.
Let’s get started!
DeconstructValueObjects (ValueBase Objects → Table)
Purpose: A basic function that recursively converts the instance tree rooted at Ancestor
into a table.
Function:
function DeconstructValueObjects(Ancestor: Instance): {any}
local Converted = {}
for _, Object in ipairs(Ancestor:GetChildren()) do
if Object:IsA("Folder") or Object:IsA("Configuration") then
Converted[Object.Name] = DeconstructValueObjects(Object)
elseif Object:IsA("ValueBase") then
Converted[Object.Name] = (Object :: ValueBase & {Value: any}).Value
end
end
return Converted
end
Parameters:
-
Ancestor
: AnInstance
object representing the root of the instance tree to convert. The function will recursively convert theValueBase
objects withinAncestor
and its descendants.
Returns:
-
table
: A dictionary containing the values of theValueBase
objects within the givenAncestor
instance and its descendants, organized into a hierarchy of sub-tables based on the structure of the instance tree.Folder
andConfiguration
instances serve as sub-table keys in the resulting table.
Code Example:
-- Assuming that we have a folder/configuration named `Stats` in the player object containing:
-- Coins, TotalPlayTime, and a sub-configuration named `Sub-Stats` that contains: `IsAlive`, and `IsAFK` Boolean Objects.
local Player = game:GetService("Players").LocalPlayer
local Stats = Player.Stats
local StatsTable = DeconstructValueObjects(Stats)
print(StatsTable)
-- The output should be like:
--[[
["Coins"] = 20,
["TotalPlayTime"] = 10,
["Sub-Stats"] = {
["IsAlive"] = true,
["IsAFK"] = false
}
]]
TableToValueObjects (Table → ValueBase Objects)
Purpose: Converts a table into a hierarchy of ValueBase
instances, with the structure of the hierarchy reflecting the structure of the input table.
The resulting hierarchy will be rooted at an instance of the specified type and parented to the specified Instance
object if provided.
Function:
function TableToValueObjects(Table: any, RootObject: (string | Instance)?, Parent: Instance?, DefaultGroupingObject: string?, Processed: any?): Instance
if Processed and Processed[Table] then
return nil
end
local Root
local DGroupingClassName = DefaultGroupingObject or ("Folder")
local Processed = (Processed or {})
Processed[Table] = true
if typeof(RootObject) == "Instance" then
Root = RootObject
elseif type(RootObject) == "string" then
local Success, Obj = pcall(function() return Instance.new(RootObject) end)
if Success then Root = Obj else Root = Instance.new("Folder") end
else
Root = Instance.new("Folder")
end
local ObjectsMapping = {
["Ray"] = "RayValue",
["integer"] = "IntValue",
["boolean"] = "BoolValue",
["number"] = "NumberValue",
["string"] = "StringValue",
["Color3"] = "Color3Value",
["CFrame"] = "CFrameValue",
["Instance"] = "ObjectValue",
["Vector3"] = "Vector3Value",
["BrickColor"] = "BrickColorValue",
}
for Key, Value in pairs(Table) do
local ValueType = typeof(Value)
local Name = (typeof(Key) ~= "Instance" and Key) or (tostring(Key))
if ValueType ~= "table" then
local Obj = Instance.new(ObjectsMapping[ValueType])
Obj.Name = Name
Obj.Value = Value
Obj.Parent = Root
else
local Children = TableToValueObjects(Value, DGroupingClassName, Root, DGroupingClassName, Processed)
if Children then Children.Name = Name end
end
end
Root.Parent = Parent
return Root
end
Parameters:
-
Table
: A table containing the values to convert intoValueBase
instances. -
RootObject
(Optional): Astring
or anInstance
object representing the type of the root instance to create for the resulting hierarchy. If not specified, aFolder
instance will be used as the root. -
Parent
(Optional): AnInstance
object to parent the resulting hierarchy to. If not specified, the hierarchy will be left unparented. -
DefaultGroupingObject
(Optional): Astring
representing the type of instance to use as the root of hierarchies created for table values in the input table. If not specified, aFolder
instance will be used. -
Processed
(Optional): A table used by the function itself to track tables that have already been converted to avoid infinite recursion.
Returns:
-
Instance
: An object representing the root of the resulting hierarchy ofValueBase
instances.
Code Example:
local Players = game:GetService("Players")
local DefaultSettings = {
["GuiTheme"] = "Dark",
["CursorIcon"] = "Dot",
["TimeFormat"] = 24,
["OwnedHouses"] = {
workspace.House_1
},
["Graphics"] = {
["DepthOfField"] = true,
["Blur"] = false,
}
}
Players.PlayerAdded:Connect(function(Player)
local Settings = TableToValueObjects(DefaultSettings, "Configuration", Player, "Configuration")
Settings.Name = "Settings"
-- ...
end)
Equals ((Table, Table) → boolean)
Purpose: Compares two tables for equality. If Recursive
is true
, the function will recursively compare the values of any nested tables it encounters. If UseMetamethodEquality
is true
, the function will use the __eq
metamethod of the tables if they have one, instead of comparing the tables’ contents.
Function:
function Equals(Table_1: {[any]: any}, Table_2: {[any]: any}, Recursive: boolean?, UseMetamethodEquality: boolean?, Cache: any?): boolean
--| Early exit statements:
if rawequal(Table_1, Table_2) then return true end --| Checking if the tables are the same (i.e., stored in the same memory location)
if #Table_1 ~= #Table_2 then return false end --| Tables are not equal if they have different lengths.
if next(Table_1) == nil and next(Table_2) == nil then return true end --| Checking if the tables are both empty.
if UseMetamethodEquality then
local MT1 = getmetatable(Table_1::any)
local MT2 = getmetatable(Table_2::any)
if MT1 and MT2 and MT1.__eq and MT2.__eq then
return Table_1 == Table_2
end
end
--| Cache is used to prevent infinit recursion. Ignored if there is no `Recursive` is false or nil.
local Cache = (Cache ~= nil and Cache) or (Recursive and {})::any
if Recursive then
if Cache[Table_1] and Cache[Table_2] then
return true
end
Cache[Table_1] = true
Cache[Table_2] = true
end
--| Comparing Table_1 to Table_2:
for Key, Value in pairs(Table_1) do
if type(Value) ~= "table" then
if Table_2[Key] ~= Value then
return false
end
else
if not Recursive then continue end
if type(Table_2[Key]) ~= "table" then return false end
if Cache[Value] and Cache[Table_2[Key]] then return true end
if not Equals(Value, Table_2[Key], true, UseMetamethodEquality, Cache) then return false end
end
end
--| Comparing Table_2 to Table_1:
for Key, Value in pairs(Table_2) do
if type(Value) ~= "table" then
if Table_1[Key] ~= Value then
return false
end
else
if not Recursive then continue end
if type(Table_1[Key]) ~= "table" then return false end
if Cache[Value] and Cache[Table_1[Key]] then return true end
if not Equals(Value, Table_1[Key], true, UseMetamethodEquality, Cache) then return false end
end
end
return true
end
Parameters:
-
Table_1
: The first table to compare. -
Table_2
: The second table to compare. -
Recursive
(Optional): A boolean value indicating whether the function should recursively compare the values of any nested tables it encounters. If not specified, the default value isfalse
. -
UseMetamethodEquality
(Optional): A boolean value indicating whether the function should use the__eq
metamethod of the tables if they have one, instead of comparing the tables’ contents. If not specified, the default value isfalse
. -
Cache
(Optional): A table used by the function itself to track tables that have already been compared to prevent infinite recursion (e.g. tables that have__index
to themself).
Returns:
-
boolean
: A boolean value indicating whether the provided tables are equal or not.
Code Example:
local Old = {
Stats = {
Health = 100,
Armor = 50,
Coins = 1000,
Kills = 50
},
Settings = {
Graphics = {
Blur = true,
DepthOfField = false,
}
}
}
local New = {
Stats = {
Armor = 50,
Health = 100,
Kills = 50,
Coins = 1000
},
Settings = {
Graphics = {
Blur = true,
DepthOfField = false,
}
}
}
print(Equals(Old, New, true)) -- Output: true
-- Changing the Blur setting:
New.Settings.Graphics.Blur = false
print(Equals(Old, New)) -- Output: true (the Recursive argument is set to false by default)
print(Equals(Old, New, true)) -- Output: false (Blur is not equal to the one inside the old table)
I hope these functions will be helpful to you!