Thread Tools Display Modes
10-20-15, 06:50 AM   #1
Nikita S. Doroshenko
A Cyclonian
 
Nikita S. Doroshenko's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2015
Posts: 45
Lua Tables - How can I check if table in table in another table in... exists?

I would like to check if this structure is exists and if it is - do nothing, else build this structure:
Lua Code:
  1. ADVANCED_INTERFACE_GLOBAL_CONSTANTS = {} -- root
  2. ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS = {}  -- child of root
  3. ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE = {}  -- child of child of  root
  4. ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION = {}   -- child of child of child of root

Normally i use this code:
Lua Code:
  1. if not ADVANCED_INTERFACE_GLOBAL_CONSTANTS then
  2.         ADVANCED_INTERFACE_GLOBAL_CONSTANTS = {}
  3.     end
  4.     if not ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS then
  5.         ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS = {}
  6.     end
  7.     if not ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE then
  8.         ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE = {}
  9.     end
  10.     if not ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION then
  11.         ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION = nil
  12.     end
But it's very long, and looks like "hardcodeing"
So I write this function:

Lua Code:
  1. function test(table)
  2.     local orig
  3. -- Lets split (table) string in to 4 strings: ADVANCED_INTERFACE_GLOBAL_CONSTANTS, STAMPS, ADVANCED_USER_INTERFACE, VERSION
  4.     for token in string.gmatch(table, '([^.]+)') do
  5. -- Then if first string is root (ADVANCED_INTERFACE_GLOBAL_CONSTANTS) we do nothing
  6.         if orig == nil then
  7.             orig = token
  8.         else
  9. -- Else, if it's not (ADVANCED_INTERFACE_GLOBAL_CONSTANTS) we concatenate strings in to (ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS), (ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE) and (ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION)
  10.             orig = orig.."."..token
  11.         end
  12. -- Here we check if table exists - then do nothing - else we build our table or table in table...
  13.         if not _G[token] or not _G[orig] then
  14. -- PS. token always == ADVANCED_INTERFACE_GLOBAL_CONSTANTS
  15.             print(orig)
  16. --OUTPUT:
  17. --FIRST RUN: ADVANCED_INTERFACE_GLOBAL_CONSTANTS
  18. --SECOND RUN: ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS
  19. --THIRD RUN: ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE
  20. --LAST RUN: ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION
  21.             _G[orig] = {}
  22.             print(_G[orig])
  23. --OUTPUT:
  24. --FIRST RUN: table: 0x24425d0
  25. --SECOND RUN: table: 0x24426c0
  26. --THIRD RUN: table: 0x2442680
  27. --LAST RUN: table: 0x2442860
  28.         end
  29.     end
  30. end
  31.  
  32. test("ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION")
  33. -- TESTING OUR test FUNCTION --
  34. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS)
  35. --OUTPUT: table: 0x1e8ab20 (Correct table)
  36. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS)
  37. --OUTPUT: nil (that mean table with STAMPS not exists :( )
  38. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE)
  39. --OUTPUT: error: input: attempt to index a nil value (field 'STAMPS')
  40. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION)
  41. --OUTPUT: error: input: attempt to index a nil value (field 'STAMPS')

Link to check this code: http://codepad.org/wGpAa1ph

The problem is that this function build strange tables and i can't print them or use them. Looks like if orig = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS (with dot), then _G[orig] = {} not woring (tables are build inside function, but outside, it's broken)

Lua Code:
  1. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS)
  2. --OUTPUT: nil instead of nil. must be table
  3. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE)
  4. --OUTPUT: error: input: attempt to index a nil value (field 'STAMPS')
  5. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION)
  6. --OUTPUT: error: input: attempt to index a nil value (field 'STAMPS')

Will very appreciate for help, or idea how I can fix this problem.

Last edited by Nikita S. Doroshenko : 10-20-15 at 06:53 AM.
  Reply With Quote
10-20-15, 07:54 AM   #2
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Building tables programmatically isn't going to save you anything, IMO. You can do the same thing you were doing with the if checks, but in a more terse manner:

Code:
        ADVANCED_INTERFACE_GLOBAL_CONSTANTS = ADVANCED_INTERFACE_GLOBAL_CONSTANTS or {}
        ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS or {}
        ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE or {}
I omitted the last one because checking if a table is nil then setting it to nil if so makes no sense to me.

EDIT: I meant "isn't going to save you anything in this case" - I've built config tables programmatically in some of my AddOns, because otherwise I'd have a bunch of copy-paste code with slightly different labels.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.

Last edited by Torhal : 10-20-15 at 08:32 AM.
  Reply With Quote
10-20-15, 07:58 AM   #3
Barjack
A Black Drake
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 89
The problem with your code is that you can't use _G to look for tables in tables like that. For example, _G["one.two.three"] is looking for a single key in the global table with the value "one.two.three", it's not looking for table "one" then finding its key "two" then finding its key "three" etc., like it would if you wrote one.two.three in code.

Whether this is a good idea or not I don't know, but you can fix your code by avoiding using the global object like that. Here's a slightly different and working example:

Code:
function generate_subtables(str)
	local base = {}
	local current = base
	for token in string.gmatch(str, '([^.]+)') do
	    local t = {}
	    current[token] = t
	    current = t
	    print(token)
	end
	return base
end

ADVANCED_INTERFACE_GLOBAL_CONSTANTS = generate_subtables("STAMPS.ADVANCED_USER_INTERFACE.VERSION")
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS)
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS)
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE)
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION)
You can evaluate it here: https://repl.it/BSKi
  Reply With Quote
10-20-15, 08:49 AM   #4
Nikita S. Doroshenko
A Cyclonian
 
Nikita S. Doroshenko's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2015
Posts: 45
Originally Posted by Torhal View Post
Building tables programmatically isn't going to save you anything, IMO. You can do the same thing you were doing with the if checks, but in a more terse manner:

Code:
        ADVANCED_INTERFACE_GLOBAL_CONSTANTS = ADVANCED_INTERFACE_GLOBAL_CONSTANTS or {}
        ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS or {}
        ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE or {}
I omitted the last one because checking if a table is nil then setting it to nil if so makes no sense to me.
Originally Posted by Barjack View Post
The problem with your code is that you can't use _G to look for tables in tables like that. For example, _G["one.two.three"] is looking for a single key in the global table with the value "one.two.three", it's not looking for table "one" then finding its key "two" then finding its key "three" etc., like it would if you wrote one.two.three in code.

Whether this is a good idea or not I don't know, but you can fix your code by avoiding using the global object like that. Here's a slightly different and working example:

Code:
function generate_subtables(str)
	local base = {}
	local current = base
	for token in string.gmatch(str, '([^.]+)') do
	    local t = {}
	    current[token] = t
	    current = t
	    print(token)
	end
	return base
end

ADVANCED_INTERFACE_GLOBAL_CONSTANTS = generate_subtables("STAMPS.ADVANCED_USER_INTERFACE.VERSION")
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS)
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS)
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE)
print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION)
You can evaluate it here: https://repl.it/BSKi
Thank you very much Torhal and Barjack! You saved my day! I very appreciate your answer and help.

Regarding to last code example, I was not able to use it, because it's not checking existing tables. For example, I already got a table with some values, I don't want to touch them or delete, instead, I would like to add some new tables in it: https://repl.it/BSKi/1
Lua Code:
  1. ADVANCED_INTERFACE_GLOBAL_CONSTANTS = {}
  2. ADVANCED_INTERFACE_GLOBAL_CONSTANTS.TEST = "TEST"
  3. function generate_subtables(str)
  4.     local base = {}
  5.     local current = base
  6.     for token in string.gmatch(str, '([^.]+)') do
  7.         local t = {}
  8. -- I tryed to add "if not current[token] then" but looks like it's a wrong way to check if table is exists.
  9.         current[token] = t
  10.         current = t
  11.         print(t)
  12.     end
  13.     return base
  14. end
  15.  
  16. ADVANCED_INTERFACE_GLOBAL_CONSTANTS = generate_subtables("STAMPS.ADVANCED_USER_INTERFACE.VERSION")
  17. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS)
  18. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS)
  19. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE)
  20. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE.VERSION)
  21. -- Next line will print nil instead of "TEST"
  22. print(ADVANCED_INTERFACE_GLOBAL_CONSTANTS.TEST) -- SHOULD PRINT "TEST"
  Reply With Quote
10-20-15, 10:06 AM   #5
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
If this is for checking if your saved variables already exist because you're worried about overwriting them, that's not something you need to do.

Your saved variables are loaded after your addon files are executed and will overwrite any variables with the same name.

If you have "ADVANCED_INTERFACE_GLOBAL_CONSTANTS = {}" in your lua file, and "ADVANCED_INTERFACE_GLOBAL_CONSTANTS" is a saved variable name, it'll just replace your empty table with the saved data when it finishes loading.
  Reply With Quote
10-20-15, 10:20 AM   #6
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
This will probably work better.

Lua Code:
  1. local function FillTableStructure(tbl,...)
  2. --  Check if sent a table and cache for return
  3.     tbl=tbl or {};
  4.     local ret=tbl;
  5.  
  6.     for i=1,select("#",...) do
  7.         local idx=select(i,...);--  Index from argument list
  8.         local entry=tbl[idx];--     Cache current entry if any
  9.  
  10. --      Make table and link to index if not exist
  11.         if not entry then
  12.             entry={};
  13.             tbl[idx]=entry;
  14.         end
  15.  
  16.         tbl=entry;--    Select entry as current table
  17.     end
  18.  
  19.     return ret;
  20. end
  21.  
  22. --  If the table was a local, you can run the function like this
  23. ADVANCED_INTERFACE_GLOBAL_CONSTANTS=FillTableStructure(ADVANCED_INTERFACE_GLOBAL_CONSTANTS,"STAMPS","ADVANCED_USER_INTERFACE");
  24.  
  25. --  If the table was a global, you technically could give it _G as the base table or use the same line above
  26. FillTableStructure(_G,"ADVANCED_INTERFACE_GLOBAL_CONSTANTS","STAMPS","ADVANCED_USER_INTERFACE");

Unlike Barjack's function, this keeps existing entries and allows numeric and object keys. It's still much less efficient than Torhal's code.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 10-20-15 at 10:32 AM.
  Reply With Quote
10-22-15, 06:38 AM   #7
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
*cough*
__________________
  Reply With Quote
10-22-15, 03:22 PM   #8
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
Originally Posted by MunkDev View Post
*cough* (Overengineering)
Originally Posted by SDPhantom View Post
It's still much less efficient than Torhal's code.
Originally Posted by Torhal View Post
Code:
ADVANCED_INTERFACE_GLOBAL_CONSTANTS = ADVANCED_INTERFACE_GLOBAL_CONSTANTS or {}
ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS or {}
ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE = ADVANCED_INTERFACE_GLOBAL_CONSTANTS.STAMPS.ADVANCED_USER_INTERFACE or {}
(10 char minimum)
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Lua Tables - How can I check if table in table in another table in... exists?

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