Thread Tools Display Modes
02-29-12, 11:23 AM   #1
Talyrius
An Onyxian Warder
 
Talyrius's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 363
Traversing Nested Tables

I really don't know where to start in trying to explain this...

I'm writing a module for my UI to help assist me in exporting my saved variables for the various addons I use in my private compilation. I plan to include this data in my UI and overwrite the addons' default saved variables upon installation.

However, some of the addons' saved variables house information that I don't want included. I could copy the data I want manually, but I'm looking for a more automated solution.

As you'll see in my code below, I have a table populated with the names of the addons and their corresponding global variable names. For addons where I don't want to copy everything, I've listed specific table branching to the data I do want.

TL;DR:
I can't wrap my head around how to traverse nested tables while also keeping track of the current path in relation to the global variable that it needs to be copied from. I hope this makes sense to someone.

lua Code:
  1. local addonName, ns = ...
  2.  
  3. --[[--------------------------------------------------------------------------------------------------------------------
  4.     Note to self:
  5.     These need to be looked over manually before import.
  6.    
  7.         Altoholic
  8.         GatherMate2
  9.         Prat-3.0
  10.         SorhaQuestLog
  11.         Squire2
  12.         Vendorizer
  13. --]]--------------------------------------------------------------------------------------------------------------------
  14.  
  15. ns.TalyriusUI.SavedVariables = {
  16.     ["_NPCScan.Overlay"] = {
  17.         "_NPCScanOverlayOptions",
  18.     },
  19.     ["AddonLoader"] = {
  20.         "AddonLoaderSV",
  21.     },
  22.     ["AdiBags"] = {
  23.         "AdiBagsDB",
  24.     },
  25.     ["Altoholic"] = {
  26.         ["AltoholicDB"] = {
  27.             ["global"] = {
  28.                 ["options"] = {
  29.                     "ShowMinimap",
  30.                 },
  31.             },
  32.         },
  33.     },
  34.     ["Ara_Broker_Guild_Friends"] = {
  35.         "AraBrokerGuildFriendsDB",
  36.     },
  37.     ["AtlasLoot_Loader"] = {
  38.         "AtlasLootLoaderDB",
  39.     },
  40.     ["AuctionLite"] = {
  41.         "AuctionLiteDB",
  42.     },
  43.     ["Bartender4"] = {
  44.         "Bartender4DB",
  45.     },
  46.     ["Bazooka"] = {
  47.         "BazookaDB",
  48.     },
  49.     ["BigWigs"] = {
  50.         "BigWigs3DB",
  51.         "BigWigs3IconDB",
  52.     },
  53.     ["BugSack"] = {
  54.         "BugSackDB",
  55.         "BugSackLDBIconDB",
  56.     },
  57.     ["Cascade"] = {
  58.         "CascadeDB",
  59.     },
  60.     ["Fizzle"] = {
  61.         "FizzleDB",
  62.     },
  63.     ["FocusInterruptSounds"] = {
  64.         "FocusInterruptSoundsDB",
  65.     },
  66.     ["GatherMate2"] = {
  67.         "GatherMate2DB",
  68.     },
  69.     ["GladiatorlosSA"] = {
  70.         "GladiatorlosSADB",
  71.     },
  72.     ["GoblinVendorFilter"] = {
  73.         "GoblinVendorFilterDB",
  74.     },
  75.     ["InFlight"] = {
  76.         "InFlightDB",
  77.     },
  78.     ["InlineAura"] = {
  79.         "InlineAuraDB",
  80.     },
  81.     ["Mapster"] = {
  82.         "MapsterDB",
  83.     },
  84.     ["MikScrollingBattleText"] = {
  85.         "MSBTProfiles_SavedVars",
  86.     },
  87.     ["Omen"] = {
  88.         "Omen3DB",
  89.     },
  90.     ["OmniCC"] = {
  91.         "OmniCC4Config",
  92.     },
  93.     ["Prat-3.0"] = {
  94.         "Prat3DB",
  95.     },
  96.     ["RealIDToons"] = {
  97.         "RID_TOONS_FORMAT",
  98.         "RID_TOONS_LOCALFORMAT",
  99.         "RID_TOONS_HIDE_IC",
  100.     },
  101.     ["Skinner"] = {
  102.         "SkinnerDB",
  103.     },
  104.     ["SorhaQuestLog"] = {
  105.         ["SorhaQuestLogDB"] = {
  106.             ["profiles"] = {
  107.                 "Default",
  108.             },
  109.         },
  110.     },
  111.     ["Squire2"] = {
  112.         ["Squire2DB"] = {
  113.             ["profiles"] = {
  114.                 "Default",
  115.             },
  116.         },
  117.     },
  118.     ["teksLoot"] = {
  119.         "teksLootDB",
  120.     },
  121.     ["TinyDPS"] = {
  122.         "tdps",
  123.         "tdpsPosition",
  124.         "tdpsFont",
  125.         "tdpsNumberOfFights",
  126.         "tdpsF",
  127.         "tdpsV",
  128.         "tdpsTextOffset",
  129.         "tdpsColorAlpha",
  130.         "tdpsVisibleBars",
  131.         "tdpsReportLength",
  132.     },
  133.     ["TipTac"] = {
  134.         "TipTac_Config",
  135.     },
  136.     ["Vendorizer"] = {
  137.         ["VendorizerCharSavedState"] = {
  138.             "autoRepair",
  139.         },
  140.     },
  141.     ["WakeSpams"] = {
  142.         "WakeSpamsDB",
  143.     },
  144.     ["XPBarNone"] = {
  145.         "XPBarNoneDB",
  146.     },
  147.     ["YssDrop"] = {
  148.         "YssDrop_DB",
  149.     },
  150. }
  151.  
  152. function ns.recursiveCopy(src, dst)
  153.     if type(src) ~= "table" then
  154.         return {}
  155.     end
  156.    
  157.     if type(dst) ~= "table" then
  158.         dst = {}
  159.     end
  160.    
  161.     for k, v in pairs(src) do
  162.         if type(v) == "table" then
  163.             dst[k] = ns.recursiveCopy(v, dst[k])
  164.         elseif type(v) ~= type(dst[k]) then
  165.             dst[k] = v
  166.         end
  167.     end
  168.    
  169.     for k, v in pairs(dst) do
  170.         if not src[k] then
  171.             dst[k] = nil
  172.         end
  173.     end
  174.    
  175.     return dst
  176. end
  177.  
  178. function ns.recursiveWipe(tbl, key)
  179.     if type(tbl) ~= "table" then
  180.         return
  181.     end
  182.    
  183.     for k, v in pairs(tbl) do
  184.         if type(v) == "table" then
  185.             recursiveWipe(v, key)
  186.         end
  187.        
  188.         if k == key then
  189.             wipe(tbl[k])
  190.         end
  191.     end
  192. end
  193.  
  194. -- This is global intentionally.
  195. function DumpSV(addon)
  196.     wipe(TalyriusUI_DumpSV)
  197.     local SV
  198.    
  199.     if addon ~= nil then
  200.         SV = {}
  201.         local valid
  202.        
  203.         for k, v in pairs(ns.TalyriusUI.SavedVariables) do
  204.             if k == addon then
  205.                 SV[k] = v
  206.                 valid = true
  207.             end
  208.         end
  209.        
  210.         if not valid then
  211.             ns.Print("DumpSV() |cFFFF0000"..FAILED.."|r")
  212.             print("    \""..addon.."\" isn't valid.")
  213.            
  214.             return
  215.         end
  216.     else
  217.         SV = ns.TalyriusUI.SavedVariables
  218.     end
  219.    
  220.     local t = {}
  221.     for k, v in pairs(SV) do
  222.         _, _, _, enabled, _, reason, _ = GetAddOnInfo(k)
  223.        
  224.         if not enabled then
  225.             tinsert(t, {[k] = reason})
  226.         end
  227.     end
  228.  
  229.     if #t > 0 then
  230.         ns.Print("DumpSV() |cFFFF0000"..FAILED.."|r")
  231.        
  232.         for i = 1, #t do
  233.             for k, v in pairs(t[i]) do
  234.                 print("    "..k..": "..v)
  235.             end
  236.         end
  237.        
  238.         return
  239.     end
  240.    
  241.     ns.Print("DumpSV() |cFF00FF00"..COMPLETE.."|r")
  242.     wipe(t)
  243.    
  244.     for k, v in pairs(SV) do
  245.         t[k] = {}
  246.        
  247.         if #SV[k] > 0 then
  248.             for i = 1, #SV[k] do
  249.                 t[k][SV[k][i]] = _G[SV[k][i]]
  250.                
  251.                 if _G[SV[k][i]] == nil then
  252.                     print("    |cFFFFA500Warning|r: "..SV[k][i].." was nil.")
  253.                 end
  254.             end
  255.         end
  256.     end
  257.    
  258.     -- Cleanup and dump the table into a global
  259.     ns.recursiveWipe(t, "profileKeys")
  260.     ns.recursiveCopy(t, TalyriusUI_DumpSV)
  261. end
  Reply With Quote
02-29-12, 04:50 PM   #2
suicidalkatt
A Rage Talon Dragon Guard
 
suicidalkatt's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2008
Posts: 331
So what I'm assuming you're trying to do is not have your users copy any saved variables and just have this addon 'store' all the data in Lua and be able to update that data easily?
  Reply With Quote
02-29-12, 06:11 PM   #3
Talyrius
An Onyxian Warder
 
Talyrius's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 363
Yes, that is what I said already.
  Reply With Quote
02-29-12, 07:34 PM   #4
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
I'd use a slightly different approach; rather than trying to generalize everything to the nth degree, I'd just write individual functions for the "special case" addons:

Code:
local SavedVariables = {
	["_NPCScan.Overlay"] = {
		["_NPCScanOverlayOptions"] = true,
	},
	["Altoholic"] = {
		["AltoholicDB"] = function(t)
			return {
				["global"] = {
					["options"] = {
						["ShowMinimap"] = t.global.options.ShowMinimap,
					}
				}
			}
		end,
	},
}

local function copyTable(src, dst)
	if type(src) ~= "table" then
		return {}
	end
	if type(dst) ~= "table" then
		dst = {}
	end
	for k, v in pairs(src) do
		if type(v) == "table" then
			dst[k] = copyTable(v, dst[k])
		elseif type(v) ~= type(dst[k]) then
			dst[k] = v
		end
	end
	for k, v in pairs(dst) do
		if type(src[k]) == nil then
			-- "not src[k]" won't work as desired for values of "false"
			dst[k] = nil
		end
	end
end

ExportedSV = {}

function ExportSV()
	for addon, svars in pairs(SavedVariables) do
		ExportedSV[addon] = ExportedSV[addon] or {}
		for name, data in pairs(svars) do
			local svar = _G[name]
			if svar then
				if type(data) == "boolean" then
					-- we want the whole thing
					if type(svar) == "table" then
						ExportedSV[addon][name] = copyTable(data)
					else
						-- not a table, just copy the value
						ExportedSV[addon][name] = svar
					end
				elseif type(data) == "function" then
					-- we only want part of it
					ExportedSV[addon][name] = data(svar)
				end
			end
		end
	end
end
If you really wanted something totally generic, you'd want to factor out the "find and copy this key at any depth" code into its own function that could be called recursively, like the "copy this table" code, but it would probably be more of a headache than it's worth. If you're already writing out by hand which keys you want to copy, it's hardly more work to just write out a quick function that gets the wanted keys and builds a skeleton table with them.

Also, I made a small change in the "copy this table" function; see the in-code comment.
  Reply With Quote
02-29-12, 09:01 PM   #5
Talyrius
An Onyxian Warder
 
Talyrius's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 363
Phanx,

Thank you for your suggestion and the provided code example. I suppose I was just making things more complicated than they needed to be.
  Reply With Quote
03-01-12, 03:36 PM   #6
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
On second thought, I'd define the "copy this table and all its sub-tables" function before you define the list of SavedVariables to copy. That way, you can use the copy function inside your custom SavedVariable functions; eg. if you want to copy the whole "AddonDB.global" node.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Traversing Nested Tables


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off