WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   Lua Tables - How can I check if table in table in another table in... exists? (https://www.wowinterface.com/forums/showthread.php?t=52823)

Nikita S. Doroshenko 10-20-15 06:50 AM

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.

Torhal 10-20-15 07:54 AM

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.

Barjack 10-20-15 07:58 AM

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

Nikita S. Doroshenko 10-20-15 08:49 AM

Quote:

Originally Posted by Torhal (Post 311493)
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.

Quote:

Originally Posted by Barjack (Post 311494)
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"

semlar 10-20-15 10:06 AM

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.

SDPhantom 10-20-15 10:20 AM

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.

MunkDev 10-22-15 06:38 AM

*cough*

SDPhantom 10-22-15 03:22 PM

Quote:

Originally Posted by MunkDev (Post 311524)
*cough* (Overengineering)

Quote:

Originally Posted by SDPhantom (Post 311497)
It's still much less efficient than Torhal's code.

Quote:

Originally Posted by Torhal (Post 311493)
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)


All times are GMT -6. The time now is 04:03 PM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI