Is it possible to iterate through a list of tables created in the script?

Posted by Bobble on Tue 30 Dec 2008 03:50 PM — 5 posts, 22,976 views.

Canada #0
Greets all,

In the script I'm working with, I've created several tables. I would like to associate several of them with a metatable I've created.

However, I would like to avoid having to manually associate each of these tables with the metatable as this is time consuming. Especially if I decide to add several more tables in the future.

I am curious if there is a way to iterate through all the tables that have been created in the script. I know that lua must store a list of its tables that have been created somewhere, is there a way to access that list?

All the tables I want to associate with my metatable end with _list. ie. name_list, class_list, cure_list. So I was thinking there must be a way to iterate through the list of tables that has been created and run an IF statement to associate them with the metatable, as so:

if string.sub(tablename, -4) == "list" then
     setmetatable(tablename, mt) -- mt is the name of the metatable I've created
end


Any thoughts on how this might be achieved?

Amended on Tue 30 Dec 2008 03:57 PM by Bobble
USA #1
Lua does not in fact store a list of created tables, although there is a list of global variables in the variable _G. In principle, you could search _G's keys to find names ending in "list", test _G[k] for type table, and then do your thing. But this is not good (see below).

Why is it so bad to associate the metatable as soon as the table is created?

my_nifty_list = {}
set_metatable(my_nifty_list, mt)


or:

function make_table()
  t = {}
  set_metatable(t, mt)
  return t
end


then when you want to create these tables:

my_nifty_list = make_table()

that's just one utility function, that you can stick somewhere; surely a function call is not time consuming or inconvenient?

Generally speaking, iterating over the list of global variables is rather flaky and dangerous, so you should avoid it as much as possible. You're introducing subtle behavior; maybe some day you'll create a new global ending in list, and that will get the metatable associated with it even though you might not have intended that.
Canada #2
Hi David!

That function is perfect. Thanks a bunch for your help!
USA #3
Just to make it a little more robust...


function withmeta(...) 
  local t = {...} 
  local meta = {__index = function() return "hi" end }
  if #t == 1 and type(t[1])=="table" then 
    return setmetatable(t[1], meta) 
  else 
    return setmetatable(t, meta) 
  end
end


This lets you do things like:

foo = withmeta (4,5,6)  -- returns a new table with those values
foo = withmeta {first = 1, second = 2} --sets the metatable on that newly constructed table.
local bar = {5,6,7}
foo = withmeta(bar) -- returns the table with meta attached
foo = withmeta ()  -- new empty table with meta



USA #4
I wouldn't make the metatable a local variable of the function. Not only is that wasteful (a copy of the same thing is created for every table), but also you can't test for metatable equality, which is something you occasionally need to do.

Also, you can't create a table that only contains a table using that method; it'll associate the metatable only with the contained table (and return that).

Otherwise, I like the idea of passing the ... argument to the table constructor.

Note that:
local bar = {5,6,7}
foo = withmeta(bar) -- returns the table with meta attached

Is setting the metatable on bar as well, because t = {bar}, and t[1]'s metatable is set. The above syntax makes it look like foo is a copy with the metatable attached, not touching the original.