String to Dictionary

I want to store a dictionary within an attribute.

I need a function that would convert a string like this:

"{ ["Logs"] = 5; ["Sticks"] = 2 }"

to an actual dictionary like this:

{
["Logs"] = 5;
["Sticks"] = 2;
}

Look into these methods:

Although you can just set each attribute in your dictionary separately.

If this is for datasaving, I suggest doing just that.

-- Get:
Data:GetAttributes() --> {Logs = 5, Sticks = 2}
-- Set:
for i, v in pairs(UserData) do Data:SetAttribute(i,v) end

If you don’t want to use JSONEncode and JSONDecode methods, I’ve already made my string → table parser recently using string patterns.

Here is my implementation:

Updated Function
--[[ ParseString - Parse a string and return a table.
-| @param   Str: The string to be parsed. Can contain numbers, strings, and nested tables.
-| @param   Separator: The separator character used to separate elements in the string. (Note: Do not use separator characters within any provided key-value especially with nested tables otherwise it will give inaccurate results)
-| @return  A table representation of the given string.
-| @example ParseString("'Hello World', test1, 1, 2") -> {[1] = "HelloWorld", [2] = "test1", [3] = 1, [4] = 2} ]]
function ParseString(Str: string, Separator: string?)
	assert(type(Str) == "string", "Invalid Argument [1]; String expected.")
	assert((type(Separator) == "string" and #Separator > 0) or Separator == nil, "Invalid Argument [2]; Separator string expected.")
	---------------------------------------------------------------------------------------------------------------------|

	local Separator = (Separator or ",")
	local KeyValuePattern = "%s*%[?[\"']?(.-)[\"']?%]?%s*=%s*[\"']?(.+)[\"']?%s*"
	local StringValuePattern = "^%s*%[?[\"']?(.-)[\"']?%]?%s*$"
	local TablePattern = "[" .. Separator .. "]*%s*([^" .. Separator .. "]+%s*=%s*{.+}%s*)"
	local TableKVPattern = "%[?[\"']?(.-)[\"']?%]?%s*=%s*{(.+)}"

	local Table: any = {}
	local NMatches:{{string | number}}= {}
	Str = string.match(Str, "^{?%s*(.-)%s*}?$")
	
	for NestedTable in string.gmatch(Str, TablePattern) do
		local Starting, Ending = string.find(Str, NestedTable, (NMatches[#NMatches] and NMatches[#NMatches][3]::number - 1) or 1, true)
		if Starting and Ending then
			NMatches[#NMatches+1] = {NestedTable, Starting, Ending}
		end
	end
		
	for _, MatchingTable in ipairs(NMatches) do
		local Pairs, Starting: any, Ending: any = MatchingTable[1], MatchingTable[2], MatchingTable[3]
		local Key, Value = string.match(Pairs::string, TableKVPattern)
		Table[string.gsub(Key, "[ " .. Separator .. "\"']+", "")] = ParseString(Value, Separator)
		Str = (string.sub(Str, 1, Starting-1) .. string.sub(Str, Ending+1))
	end
	
	for Sub in string.gmatch(Str, "([^".. Separator .."]+)") do
		local IsBlank = string.match(Sub, "^%s*$") ~= nil
		local NumberValue = tonumber(Sub)
		local StringValue = string.match(Sub, StringValuePattern)
		local Key, Value = string.match(Sub, KeyValuePattern)
		
		if IsBlank then
			continue
			
		elseif NumberValue then
			Table[#Table+1] = NumberValue

		elseif StringValue and not string.match(Sub, "=") then
			Sub = string.match(Sub, "^%s*%[?[\"']?(.-)[\"']?%]?%s*$")
			Table[#Table+1] = Sub

		elseif Key and Value then
			Key = tonumber(Key) or Key
			Value = tonumber(Value) or Value
			Table[Key] = Value
		end
	end
	return Table
end
Old
--[[ ParseString - Parse a string and return a table.
---| This function parses a string and returns a table representation of it. It is not 100% accurate but at least it works :d (it was really tough writing it).
---| The string can contain numbers, strings, and nested tables.
---| The separator parameter specifies the character used to separate elements in the string (Note: Please do not use separator characters within any provided key-value especially with nested tables).
-| @param   Str: The string to be parsed.
-| @param   Separator: The separator character used to separate elements in the string.
-| @param   UseLoadstring: A boolean value indicating whether to use the loadstring function to parse the string.
-| @return  A table representation of the string.]]
function ParseString(Str: string, Separator: string?, UseLoadstring: boolean?)
	assert(type(Str) == "string", "Invalid Argument [1]; String expected.")
	assert((type(Separator) == "string" and #Separator > 0) or Separator == nil, "Invalid Argument [2]; String expected.")
	--------------------------------------------------------------------------------------------------------|

	local Separator = (Separator or ",")
	if UseLoadstring then return (loadstring("return " .. Str)::any)() end

	local KeyValuePattern = "%s*(%[*[\"']*%]*[%s%w%p_]+%[*[\"']*%]*)%s*=%s*(%[*[\"']*%]*[%s%w%p_]+%[*[\"']*%]*)%s*$"
	local StringValuePattern = "%s*(%[*[\"']*%]*[%s%w%p_]+%[*[\"']*%]*)%s*"
	local TablePattern = "([" .. Separator .. "]*%s*[^" .. Separator .. "]+%s*=%s*{.+}%s*)"
	local TableKVPattern = "%[*[\"']*([%w%p%s_]+)[\"']*%]*%s*=%s*{([%w%p%s_]+)}"

	local Table: any = {}
	local NMatches:{{string | number}}= {}

	for NestedTable in string.gmatch(Str, TablePattern) do
		local Starting, Ending = string.find(Str, NestedTable, (NMatches[#NMatches] and NMatches[#NMatches][3]::number - 1) or 1, true)
		if Starting and Ending then
			NMatches[#NMatches+1] = {NestedTable, Starting, Ending}
		end
	end

	for _, MatchingTable in ipairs(NMatches) do
		local Pairs, Starting: any, Ending: any = MatchingTable[1], MatchingTable[2], MatchingTable[3]
		local Key, Value = string.match(Pairs::string, TableKVPattern)
		Table[string.gsub(Key, "[ " .. Separator .. "\"']+", "")] = Table.ParseString(Value, Separator)
		Str = (string.sub(Str, 1, Starting-1) .. string.sub(Str, Ending+1))
	end

	for Sub in string.gmatch(Str, "([^".. Separator .."]+)") do
		if tonumber(Sub) then
			Table[#Table+1] = tonumber(Sub)

		elseif Sub:match(StringValuePattern) and not Sub:match("=") then
			Sub = Sub:gsub(StringValuePattern, "%1"):gsub("[%[%]'\"]", "")
			Table[#Table+1] = Sub

		elseif Sub:match(KeyValuePattern) then
			for Cap1, Cap2 in string.gmatch(Sub, KeyValuePattern) do
				if not Cap1:match("[%[%]'\"]") then
					Cap1 = Cap1:gsub("%s", "")
				end
				Cap2 = Cap2:gsub("[%[%]'\"]", "")
				Cap1 = Cap1:gsub("%s*(%[[^%[%]]+%])%s*", "%1"):gsub("[%[%]'\"]", "")
				Table[Cap1] = Cap2
			end
		end
	end
	return Table
end
1 Like

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