How can I turn a table to a string

Ok for starters, yes I did search for it, but didnt understand the posts.

So I could use :JsonEncode(), but I want the code to be readable by well the script ig.
I found a post that asked this but honestly didnt understand a thing they were saying for some reason.

So is there anyway to do this? I mean there obv is a way, but how?? DUN DUN DUN

1 Like

table.concat will turn the table into one string.

local t = {
    "Hello",
    "World"
}

print(table.concat(t)) --> "HelloWorld"
print(table.concat(t, ",")) --> "Hello,World"
print(table.concat(t, "", 2)) --> "World"

It has some pretty useful parameters.

1 Like

Hmm am I doing something wrong? It seems to print nothing every time

	local stringData = table.concat(data)
	print(data) -- data prints as a table
	print(stringData) -- prints nothing

Wait does this not work with dictionaries? I probably should’ve thought of that, any dictionary alternatives?

1 Like

Why do you want to convert it to a string in the first place? The only thing I can think of that isn’t too hardcoded is to use :JsonDecode() on the encoded string in order to turn it back into a lua table.

1 Like
local function tableToString(tbl)
    local result = "{"
    for i, v in ipairs(tbl) do
        result = result .. tostring(v)
        if i < #tbl then
            result = result .. ", "
        end
    end
    result = result .. "}"
    return result
end

print(tableToString(TABLE-NAME))

It might be a good idea to tell your use case. Also, what do you mean by “I want the code to be readable by well the script ig”? As @JohnyGamingAltAcc said, you can convert a json string back to a table with :JsonDecode. That’s what I would first assume “readable by the script” to mean.

If your goal is to compress the data and later convert it back to the original data then JsonEncode and JsonDecode are not the right approach since json strings are meant to be human readable, not memory efficient. The best way to compress the data depends on the situation.

If you mean that you want to be able to use the returned string in a script’s source code as a table constructor, then the function convertTableToLuauTableConstructor in the code below could work for you. There are more datatypes that this could support but I didn’t want to include a huge number of datatypes when I don’t even know if this is what you are looking for. A lot of the code is red in this post because devforum code formatting isn’t compatible with Luau string interpolation.

Here's the code with the conversion function.
--!strict
local function getInstancePath(instance: Instance): string
	if instance ~= game and not instance:IsDescendantOf(game) then
		error("This Instance is not the DataModel or a descendant of the DataModel so it is not possible to get an instance hierarchy path for it.")
	end
	local fullName: string = ""
	local current: Instance = instance
	while current ~= game do
		for _, siblingOrSelf: Instance in (current.Parent :: Instance):GetChildren() do
			if siblingOrSelf == current then
				continue
			end
			if siblingOrSelf.Name == current.Name then
				error("Ambiguous path.")
			end
		end
		local nameString: string = if string.match(current.Name, "%s") ~= nil then `[\"{current.Name}\"]` else `.{current.Name}`
		fullName = nameString .. fullName
		current = current.Parent :: Instance
	end
	fullName = "game" .. fullName
	return fullName
end

local function convertToString(value: any): string
	if typeof(value) == "string" then
		return `\"{value}\"`
	elseif typeof(value) == "number" then
		return tostring(value)
	elseif typeof(value) == "boolean" then
		return tostring(value)
	
	elseif typeof(value) == "Instance" then
		return getInstancePath(value)
		
	elseif typeof(value) == "Vector2" then
		return `Vector2.new({value.X}, {value.Y})`
	elseif typeof(value) == "Vector2int16" then
		return `Vector2int16.new({value.X}, {value.Y})`
	elseif typeof(value) == "Vector3" then
		return `Vector3.new({value.X}, {value.Y}, {value.Z})`
	elseif typeof(value) == "Vector3int16" then
		return `Vector3int16.new({value.X}, {value.Y})`
	elseif typeof(value) == "CFrame" then
		local x: number, y: number, z: number, R00: number, R01: number, R02: number, R10: number, R11: number, R12: number, R20: number, R21: number, R22: number = value:GetComponents()
		return `CFrame.new({x}, {y}, {z}, {R00}, {R01}, {R02}, {R10}, {R11}, {R12}, {R20}, {R21}, {R22})`
	elseif typeof(value) == "UDim" then
		return `UDim.new({value.Scale}, {value.Offset})`
	elseif typeof(value) == "UDim2" then
		return `UDim2.new({value.X.Scale}, {value.X.Offset}, {value.Y.Scale}, {value.Y.Offset})`
	elseif typeof(value) == "Color3" then
		return `Color3.new({value.R}, {value.G}, {value.B})`
	
	elseif typeof(value) == "Enums" then
		return "Enum"
	elseif typeof(value) == "Enum" then
		return `Enum.{tostring(value)}`
	elseif typeof(value) == "EnumItem" then
		return tostring(value)
	else
		error(`Unhandled datatype: {typeof(value)}`)
	end
end

local function getKeyString(key: any): string
	if typeof(key) == "string" then
		return if string.match(key, "%s") == nil then key else `[{convertToString(key)}]`
	end
	return `[{convertToString(key)}]`
end

local function convertTableToLuauTableConstructorRecursive(tabl: {[any]: any}, multiline: boolean, depth: number): string
	local indentation: string = "\t"
	local minimumIndentation: string = string.rep(indentation, depth)
	local separator: string = if multiline then `,\n{minimumIndentation .. indentation}` else ", "
	local str: string = if multiline then (if depth == 0 then `\n\{\n{minimumIndentation .. indentation}` else `\{\n{minimumIndentation .. indentation}`) else "{"
	local loopIsCurrentlyInArrayPart: boolean = true
	local previousArrayIndex: number = 0
	for key: any, value: any in tabl do
		if loopIsCurrentlyInArrayPart and key ~= previousArrayIndex + 1 then
			loopIsCurrentlyInArrayPart = false
		else
			previousArrayIndex = key :: number
		end
		
		local valueString: string = if typeof(value) == "table" then convertTableToLuauTableConstructorRecursive(value, multiline, depth + 1) else convertToString(value)
		if loopIsCurrentlyInArrayPart then
			str ..= valueString
		else
			local keyString: string = if typeof(key) == "table" then `[{convertTableToLuauTableConstructorRecursive(key, multiline, depth + 1)}]` else getKeyString(key, loopIsCurrentlyInArrayPart)
			str ..= `{keyString} = {valueString}`
		end
		if next(tabl, key) ~= nil then
			str ..= separator
		end
	end
	str ..= if multiline then (if depth == 0 then `\n{minimumIndentation}\}\n` else `\n{minimumIndentation}\}`) else "}"
	return str
end

local function convertTableToLuauTableConstructor<TKey, TValue>(dictionary: {[TKey]: TValue}, multiline: boolean): string
	return convertTableToLuauTableConstructorRecursive(dictionary, multiline, 0)
end
Here's some code that I used for testing it.
local part: Part = Instance.new("Part")
local childPart: Part = Instance.new("Part")
childPart.Name = "Child Part"
local descendantPart: Part = Instance.new("Part")
descendantPart.Name = "DescendantPart"
descendantPart.Parent = childPart
childPart.Parent = part
part.Parent = workspace

local tabl: {[any]: any} = {
	"number1",
	"number2",
	"number3",
	hi = 1.5,
	[3.25] = CFrame.new(9, 9, 9),
	[descendantPart] = "bye",
	[UDim2.new(1, 2, 3, 4)] = Vector3.new(3, 2, 1),
	[UDim.new(1, 2)] = Vector2.new(2, 1),
	[Vector2int16.new(1, 2)] = Vector3int16.new(2, 1),
	nestedTable = {
		something = Enum,
		["something else"] = Enum.KeyCode,
		[Enum.KeyCode.H] = Color3.new(.5, .5, .5),
		[true] = false,
		moreDeeplyNestedTable = {
			somethingVeryDeep = "This is deep."
		}
	},
	[{
		hi = "hi"
	}] = {
		bye = "bye"
	}
}

print(convertTableToLuauTableConstructor(tabl, true))
1 Like

see I want the table as a readable string, doing jsonEncode turns it to a modified string, so instead of equal signs theres :.

Now thinking about this now I think I should probably just use a folder lol, itd save me time

I just told you that :JsonDecode can turn an encoded table back into lua…

Could you show an example input and the expected output for that input? That would make it easier to understand what you mean by “readable string”.

Json strings are meant to be simple and human-readable so if you don’t consider them readable then I have no idea what kind of string you are expecting.

ok but I need the string readable, I dont need to turn it into lua, as I dont need it as lua, I need it as a string, a string I can read.

Im well aware of what JsonDecode and Encode does, as I use them all the time, they are not what I need. As I said here:

it converts the table to a string yes, but it replaces a lot of the symbols with other ones, and therefore DOESNT WORK how I need it.

if you read my message you wouldve got that maybe (My explanation was pretty bad so Ig I dont blame you)

“Readable” is quite a vague description.

What valuetypes are the keys and the values in the table? Or is that unknown?

You’ve mentioned that JsonEncode uses colons instead of equality signs but you said “it replaces a lot of the symbols with other ones” so I assume colons are not the only problem. What other undesired symbols does it use? Are the square brackets (array) or curly brackets (dictionary) at the start and end a problem?

And, as mentioned before, it would be useful if you could give an example input and expected output for it.

I wrote a very simple function for converting a table to a string. However, it may not be what you want because only things I know about your requirements are the following:

  • You want the string to have an equality sign between the key and the value.
  • You want the string to be easy to read for a human.

If the string contains vectors then i’d say that the readability would be better with parentheses around the numbers but I don’t know what kind of values it contains so I don’t know what format would be the most readable.

Anyways, here's the function.
local function convertTableToString(tabl: {[any]: any}): string
	local str: string = ""
	for key, value in tabl do
		str ..= `{key} = {value}`
		if next(tabl, key) ~= nil then
			str ..= ", "
		end
	end
	return str
end

hmm well as I have realized from my situation using a folder would be best cause of what I was doing so I dont need this anymore, thanks though