Thread Tools Display Modes
05/11/14, 09:39 PM   #1
RavenDT
 
RavenDT's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 6
Exclamation Pairs in Table not returning keys

Greetings, fellow ESOUI brethren.

It seems I am having trouble attempting to empty out a nested table structure in my addon and I was wondering if anyone else may have encountered the same problem as I.

Apparently, there's no way to delete or unassign a table, but I can set all the elements to nil. However, iterating over pairs isn't working. When I ask for pairs:

Code:
for k in pairs (table) do
    d(table[k])
end
I get errors. When I do:

Code:
/script d(table)
I get:

Code:
.(function): GetInterfaceForCharacter = function: 9F3AA8E0
.(table): default = table: 9F3AA7A0
But I know the table structure has non-consecutive numbers for keys. If I do a /script d(table[key]) I get the value.

Does anyone know how to fix this issue? I'd greatly appreciate it!

V/R,
-Raven
  Reply With Quote
05/11/14, 11:40 PM   #2
CatoTheElder
Join Date: May 2014
Posts: 44
Hey Raven,

In modern versions of lua, garbage collection is automagically done. However, for optimal performance, you may want to run it manually. I hope this is what you're looking for.

-- variable declaration and memory allocation
tblBigTable = {}

-- populate the table
for i=1,100000 do
tblBigTable[i] = i*2
end

-- set the table as 'garbage'
tblBigTable = nil

-- automatic garbage collection will take care of it... eventually OR
-- manual garbage collection, to free up all that memory
collectgarbage("collect")
Lua Manual Reference:
http://www.lua.org/manual/5.1/manual...collectgarbage

Cheers!
  Reply With Quote
05/11/14, 11:44 PM   #3
skyraker
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 154
Originally Posted by RavenDT View Post
Apparently, there's no way to delete or unassign a table, but I can set all the elements to nil. However, iterating over pairs isn't working. When I ask for pairs:

Code:
for k in pairs (table) do
    d(table[k])
end
I always thought you needed two parameters for pairs, even if one isn't used?

Lua Code:
  1. for k,_ in pairs(table) do
  2.    d(table[k])
  3. end
  Reply With Quote
05/12/14, 04:37 PM   #4
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
I always use two parameters for a pairs loop. Give that a try. Can't think right now and have to run, there would be another way to do it if that won't work.

Oh - and DON'T force the garbage collector to run.
  Reply With Quote
05/12/14, 05:36 PM   #5
stjobe
 
stjobe's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 60
Post

I'm not sure what you're trying to achieve, as nilling the table effectively deletes it (it will be garbage collected eventually).

Lua Code:
  1. for k,_ in pairs(someTable) do
  2.     someTable[k] = nil
  3. end
should do the trick; someTable is now empty and will be disposed of in time.

As for printing out the values of your table containing tables and functions, there's a slight hitch; you can't get the source code for functions back after compilation to byte-code. Tables should be easy enough though:

Example:
Lua Code:
  1. t = {}
  2.  
  3. function t.someFunction()
  4.     local var = 13
  5.     print(var)
  6. end
  7.  
  8. t.someTable = {}
  9. t.someTable.alpha = "tableEntry 1"
  10. t.someTable.beta = "tableEntry 2"
  11.  
  12. for k,v in pairs(t) do
  13.     print(k) -- gives the key, e.g. "someTable" or "someFunction"
  14.     print(v) -- gives the value, e.g. "table: 0x04E50778" or "function: 0x04EF298"
  15.     print(t[k]) -- same as "print(v)"
  16.     if type(v) == "table" then -- we can iterate over tables
  17.         for l,_ in pairs(v) do
  18.             print(l..": "..v[l])
  19.         end
  20.     elseif type(t[k]) == "function" then -- but not over functions
  21.         print("source code isn't available")
  22.     end
  23.     print("---")
  24. end
Which should output something similar to:
Code:
someTable
table: 0x04C3E578
table: 0x04C3E578
alpha: tableEntry 1
beta: tableEntry 2
---
someFunction
function: 0x04E518F8
function: 0x04E518F8
source code isn't available
---
Also, you don't strictly need two variables to pairs(), but it's considered good practice to replace the unneeded variable with underscore. The following three snippets do the same thing:
Lua Code:
  1. for k,v in pairs(t) do
  2.     t[k] = nil
  3. end
  4.  
  5. for k in pairs(t) do
  6.     t[k] = nil
  7. end
  8.  
  9. for k,_ in pairs(t) do
  10.     t[k] = nil
  11. end
It's the same for any function; you don't have to store all their return values - you'll get as many from the start of the list of return values as you declare variables for, and the rest will be ignored. In most cases it's proper to replace the unneeded return values with underscore, but in other cases that just makes for unreadable code.

For instance, GetUnitBuffInfo() returns ten values, but if I'm only interested in the name of the buff (which is first on the list of return values), it's perfectly fine to use it like this:
Lua Code:
  1. local buffName = GetUnitBuffInfo("player", 1)
instead of the slightly ridiculous
Lua Code:
  1. local buffName, _, _, _, _, _, _, _, _, _ = GetUnitBuffInfo("player", 1)
or even more ridiculous naming nine variables I won't ever use.

It gets a bit messier if I'm only interested in the last return value though...

Last edited by stjobe : 05/12/14 at 06:08 PM. Reason: Expanded pairs argument a bit.
  Reply With Quote
05/12/14, 06:18 PM   #6
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
If that code fragment is exactly the same and not just an example, then ditch the table name of "table". Use something else. Table is a special "word" in LUA.

Also, if you decide to use two parameters for pairs/ipairs/next, take advantage of it. All of these below are equivalent:

Code:
for k in next, renamedTable do
d(renamedTable[k])
end

for k, v in next, renamedTable do
d(v)
end

for k in pairs(renamedTable) do
d(renamedTable[k])
end

for k, v in pairs(renamedTable) do
d(v)
end
Note that "ipairs" is not up there. It is different than "pairs". "next" is basically included in the pairs, as I've said, all above will give you the same results, and you can use "local next = next" to speed up the process a little bit (by nanoseconds :P)

Last edited by lyravega : 05/12/14 at 06:22 PM.
  Reply With Quote
05/12/14, 06:23 PM   #7
Roupine
Join Date: May 2014
Posts: 18
Originally Posted by stjobe View Post
For instance, GetUnitBuffInfo() returns ten values, but if I'm only interested in the name of the buff (which is first on the list of return values), it's perfectly fine to use it like this:
Lua Code:
  1. local buffName = GetUnitBuffInfo("player", 1)
instead of the slightly ridiculous
Lua Code:
  1. local buffName, _, _, _, _, _, _, _, _, _ = GetUnitBuffInfo("player", 1)
or even more ridiculous naming nine variables I won't ever use.

It gets a bit messier if I'm only interested in the last return value though...

You could wrap it in {} to get a table and index it from there:
Lua Code:
  1. local buffInfo = {GetUnitBuffInfo("player",1)}
  Reply With Quote
05/12/14, 06:46 PM   #8
stjobe
 
stjobe's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 60
Originally Posted by Roupine View Post
You could wrap it in {} to get a table and index it from there:
Lua Code:
  1. local buffInfo = {GetUnitBuffInfo("player",1)}
I could, but that would still leave nine unnecessary table entries.

The correct way (which I just learned and want to share) of getting the last value from GetUnitBuffInfo() is using select():
Lua Code:
  1. local lastValue = select(10, GetUnitBuffInfo("player", 1))
http://www.lua.org/manual/5.1/manual.html:
select (index, ···)
If index is a number, returns all arguments after argument number index. Otherwise, index must be the string "#", and select returns the total number of extra arguments it received.
Here's an example showing the difference:
Lua Code:
  1. function manyReturns()
  2.     return "alfa", "beta", "gamma"
  3. end
  4.  
  5. t = {manyReturns()}
  6.  
  7. for k,v in pairs(t) do
  8.     print(k..": "..v)
  9. end
  10.  
  11. v = select("#", manyReturns())
  12. print(v)
  13.  
  14. r = select(v, manyReturns())
  15. print(r)
Which outputs:
Code:
1: alfa
2: beta
3: gamma
3
gamma
Not so messy after all

Last edited by stjobe : 05/12/14 at 06:57 PM. Reason: added example of select
  Reply With Quote
05/12/14, 07:44 PM   #9
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
Originally Posted by stjobe View Post
The correct way (which I just learned and want to share) of getting the last value from GetUnitBuffInfo() is using select():
Actually, using select() is more expensive (since it's a function call) than just assigning the values - that are already returned anyway, even in the select() call - to a throwaway variable. If your code is only called once, or not very often, then you can use select() if you think it makes your code easier to read and follow. But I would use throwaway variables if it is called in an event that fires often or in an OnUpdate script.

------

Clearing out a table still leaves an empty table behind, which you can reuse. Depending on the size of your table and how often this is done, you should weigh the pros and cons of clearing and reusing a table as compared with just creating a new table and letting the garbage collector pick up the old one on the next pass.
  Reply With Quote
05/12/14, 10:32 PM   #10
RavenDT
 
RavenDT's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 6
Okay, I kinda found a work-around, but...

So, with using Zgoo, I was able to find out some interesting information.

When you grab SavedVariables with:

Code:
myVar = ZO_SavedVars:NewAccountWide("mySavedVars", ... )
It only assigns a reference to myVar. In reality, your SavedVariables all get loaded into one big table whenever your addon loads.

So while I had "myTable" that I was working with trying to clean out and failing miserably, it's because everything I've thrown at it hasn't been able to affect the reference in any way. I can't even get keys from it because it only returns the meta information.

But, when doing the same thing with myAddon_SavedVars.myTable["@Username"]["$AccountWide"], it works. Pairs works, and nil assignment to clear the table out, everything works.

However, it's a really crappy way to do business. Does anyone know how to force the table to be dereferenced? Or am I stuck in using this workaround?
  Reply With Quote
05/12/14, 10:42 PM   #11
Aiiane
 
Aiiane's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 19
Ah, the fact that you're trying to work with saved variables is a crucial bit of information that you hadn't previously mentioned.

If you're trying to completely clear out SVs, then you'll want to work with the raw variable.
  Reply With Quote
05/13/14, 05:29 AM   #12
lyravega
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 93
Pretty much everyone uses that workaround. I just go the longer way, and manually set old, unused stuff to nil so that I can see what I've done in the past. On wishlist, I wished for a command/function that'd clear the saved variables.
  Reply With Quote
05/13/14, 06:42 AM   #13
CatoTheElder
Join Date: May 2014
Posts: 44
Originally Posted by Seerah View Post
I always use two parameters for a pairs loop. Give that a try. Can't think right now and have to run, there would be another way to do it if that won't work.

Oh - and DON'T force the garbage collector to run.
I love how when I show someone how to do things they thought impossible, and include examples and reference links, you blindly state not to. I wish I could give you a gold star for your helpfulness.

--
Sorry I didn't explicitly state it (although it is in the manual I linked), but all references to data must be dereferenced before the memory is freed.
  Reply With Quote
05/13/14, 07:29 AM   #14
Wobin
 
Wobin's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2014
Posts: 78
Generally, the point is you don't need to run the garbage collector manually because it will be cleaned up as part of the normal routine of the Lua system. Forcing a GC cycle means you affect the entire system as it sets aside cycles to do a complete sweep, something it already has scheduled for in the near future.

Essentially, unless you are creating thousands of tables and niling them all out in one fell swoop, the gains from forcing a GC pale against the hit it gives to performance overall. It's unnecessary optimisation, your discarded tables will be cleaned up with the next GC cycle along with everything else.

And given that this whole situation is a once off application (ie, you're clearing the tables once and not doing it again), manual GC is unnecessary.
  Reply With Quote
05/13/14, 07:39 AM   #15
ingeniousclown
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 122
Originally Posted by CatoTheElder View Post
I love how when I show someone how to do things they thought impossible, and include examples and reference links, you blindly state not to. I wish I could give you a gold star for your helpfulness.

--
Sorry I didn't explicitly state it (although it is in the manual I linked), but all references to data must be dereferenced before the memory is freed.
Why would you assume he thought it impossible? Why would you assume that what he wanted to do was to free the memory? I mean, seeing that he wants to set stuff to nil would lead to a pretty good guess, but...

Aaaalso your link and examples do not explain nor state anywhere that the garbage collector SHOULD be run manually, only that it CAN. And just because you can do something, does not mean you should. Forcing the GC to run in any programming is an optimization technique that should be avoided unless it can't. In the world of add-on development, I can't think of any case in which the GC would ever NEED to be run, except in the most ridiculously complicated add-ons (which itself might have its own issues).

*also, Wobin is awesome.
  Reply With Quote
05/13/14, 12:33 PM   #16
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Feb 2014
Posts: 648
Originally Posted by CatoTheElder View Post
you blindly state not to. I wish I could give you a gold star for your helpfulness.
I wasn't blind when I said that. I'm pretty sure I was wearing my reading glasses. I was basing that comment off of years of writing addons for WoW (and now ESO). Thanks to Wobin and ingeniousclown for explaining since, as I had said, I didn't have much time to chat when I wrote that yesterday.

I'll add your gold star to my collection! Thanks!
  Reply With Quote

ESOUI » AddOns » AddOn Help/Support » Pairs in Table not returning keys

Thread Tools
Display Modes

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