# String split and tables

I have a table structured like this:

UnitScanDB = {
["profiles"] = {
["Goblin"] = {
["Bars"] = {
{
{
["track"] = "Cloak of Shadows#Anti-Magic Shell#Hand of Freedom#Spell Reflection",
}, -- [1]
{
["track"] = "..."
}, -- [2]
{
["track"] = "..."
}, -- [3]
}
}
}
}
}


And I would like to convert it to this format:

UnitScanDB = {
["profiles"] = {
["Goblin"] = {
["Bars"] = {
{
{
{
}, -- [1]
{
["track"] = "Anti-Magic Shell",
}, -- [2]
{
["track"] = "Hand of Freedom",
}, -- [3]
{
["track"] = "Spell Reflection",
}, -- [4]
}, -- [1]
{
--stuff
}, -- [2]
{
--stuff
}, -- [3]
}
}
}
}
}


To do this automatically, I created this function:

function UnitScan:updater()
local list = db:GetProfiles()
for p, name in ipairs(list) do
local icon = db.profiles[list[p]]["Bars"]
for x, value in ipairs(icon) do
for y, value in ipairs(icon[x]) do
if (icon[x][y].track) then
for aura in string.gmatch(icon[x][y].track,"[^#]+") do
t = {}
t.track = aura
tinsert(icon[x][y], t)
end
end
end
end
end
end


Where

• db = UnitScanDB
• db:GetProfiles() = function returns all the keys in "profiles"

Is there a more elegant way to do this without using all those cycles, perhaps using the metatables?

IMO, there is a cleaner way to do this without even involving metatables. You have a bunch of tables that are nested together that contains some data on each level and you're looking to access and possibility modify a particular branch.

This is not that different from say a HTML DOM tree. This means you can use any standard tree traversal algorithm to find what you're looking for. For example, a table_gsub function(can't think of a better name atm), it takes a table, a string-key you're looking for, and a function to call when a match is found:

function table_gsub(t, selector, f)
local visit = {}
for k, v in pairs(t) do
if type(v) == 'table' then
table.insert(visit, v)
end
end
if t[selector] then f(t) end
for _, each in ipairs(visit) do
table_gsub(each, selector, f)
end
end


And a convert function to your desire format:

local function convert_format(t)
assert(type(t.track) == 'string')
for each in t.track:gmatch "%w[^#]+" do
table.insert(t, {track = each})
end
t.track = nil
end


A simple test to make sure it does the right thing:

table_gsub(profiles, "track", convert_format)
dump(profiles)


It should output something like:

profiles = {
Goblin = {
Bars = {
{
{
{
},
{
track = "Anti-Magic Shell"
},
{
track = "Hand of Freedom"
},
{
track = "Spell Reflection"
}
},
{
track = "..."
},
{
track = "..."
}
}
}
}
}


You could of course take this idea further with multiple key-string selectors to match against giving you something like jquery-style lua table selection.