Reputation: 21
I've asked several people and searched Google but haven't found an answer for this; is there a way to return the index of a variable in a table in Lua? I have a callback function that returns a variable that will always be part of a table. In the callback it is just called something like "shape_one", but the shape itself will be one of a table of shapes and will look like this: Objects.shapes[4].
If the "shape_one" has the value of Objects.shapes[4] then is there a way to return the 4?
I hope I was clear enough.
Upvotes: 2
Views: 11118
Reputation: 111
I know it was used for something else, but it's applicable to this circumstance too.
shapes = {} -- Create your table, can be called anything
shapes.r_index = {} -- Holds the number value, i.e. t[1] = 'Foo'
shapes.r_table = {} -- Holds the string value, i.e. t['Foo'] = 1
mt = {} -- Create the metatable
mt.__newindex = function (self, key, value) -- For creating the new indexes
if value == nil then -- If you're trying to delete an entry then
if tonumber(key) then -- Check if you are giving a numerical index
local i_value = self.r_index[key] -- get the corrosponding string index
self.r_index[key] = nil -- Delete
self.r_table[i_value] = nil
else -- Otherwise do the same as above, but for a given string index
local t_value = self.r_table[key]
self.r_index[t_value] = nil
self.r_table[key] = nil
end
else
table.insert(self.r_index, tonumber(key), value) -- For t[1] = 'Foo'
self.r_table[value] = key -- For t['Foo'] = 1
end
end
mt.__index = function (self, key) -- Gives you the values back when you index them
if tonumber(key) then
return (self.r_index[key]) -- For For t[1] = 'Foo'
else
return (self.r_table[key]) -- For t['Foo'] = 1
end
end
setmetatable(shapes, mt) -- Creates the metatable
shapes[1] = "Circle" -- Set the values
shapes[2] = "Square"
print(shapes[1], shapes[2]) -- And *should* proove that it works
print(shapes['Circle'], shapes['Square'])
shapes[1] = nil
print(shapes[1], shapes[2]) -- And *should* proove that it works
print(shapes['Circle'], shapes['Square'])
With this you should be able to access and modify the values. It uses numerical indexes so you may have to change that bit if that's not what you want. This will allow you to make new keys and get the values using one variable; it may not be the most efficient of implementations however.
Upvotes: 0
Reputation: 5159
The most efficient way to do it would to use shape_one as the key:
local shapes = { }
local function callback(shape)
-- if you wanted to remove shape_one from the table:
shapes[shape] = nil
-- or if you want verify it's in there:
print(shapes[shape] ~= nil)
end
local the_shape = { is_circle = false }
shapes[the_shape] = true
callback(the_shape)
for shape, _ in pairs(shapes) do
callback(shape)
end
Lua uses hashing to ensure each table acts a unique key, and the algorithm it uses is very quick (doesn't iterate over the table like you would in Alexander Gladysh's solution).
Upvotes: 1
Reputation: 41493
Is this your case?
local shapes = { }
local function callback(shape_one)
-- ???
end
local the_shape = { is_circle = false }
shapes[4] = the_shape
assert(callback(the_shape) == 4)
Both shapes[4]
and the_shape
contain a reference to value, but in Lua there is no other connection between these two variables. So, you can not say "index of a variable in a table", you should rather say "index of a value in a table, that matches the value in a variable". What exactly "matches" is depends on your case. In this case you most likely looking for reference equality.
Note that in Lua all table values are unique, so the_shape ~= { is_circle = false }
(that is, new table with identical content), but the_shape == shapes[4]
(both refer to the same value). You can compare tables by value if needed, but that's a separate topic.
So, if you really want to find an index of the value in table, you have to search it there manually. Either do a linear search:
local function callback(shape_one)
for k, v in pairs(shapes) do
if v == shape_one then
return k
end
end
return nil, "shape not found" -- or maybe call error() here
end
...Or cache all shapes:
local function tflip(t)
local r = { }
for k, v in pairs(t) do
r[v] = k -- overrides duplicate values if any
end
return r
end
local shape_index = tflip(shapes)
local function callback(shape_one)
return shape_index[shape_one] -- will return nil if not found
end
Note that shape_index
would, of course, prevent garbage collection of its contents. Assuming its lifetime is the same as lifetime of shapes
table and it is kept in sync with it, this is not a problem. If this is not your case, you may configure the shapes
table to be weak-keyed. (Tell me if you want me to expand this point.)
BTW, you may keep shape_index up to date automatically with some metatable magic. Tell me if you want this explained as well, I will update the answer.
Upvotes: 3