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"] = {
                {
                    {
                        {
                            ["track"] = "Cloak of Shadows",
                        }, -- [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 = "Cloak of Shadows"
          },
          {
            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.