Editing a subtable inside a maintable using a function inside the maintable - lua

Is it possible to edit the contents of a table which is inside another table using a function?
local MainTable = {
subtable = {
x = 0,
y = 0
},
addX = function()
subtable.x = subtable.x + 1
end
}
I'm getting the error attempt to index ? (a nil value)
Is it possible to achieve this?
It works outside the table, I used:
print(MainTable.subtable.x+1)
How come it doesn't work inside the table? Does tables being objects play a role?
Thank you!

Lua tables aren't objects; just because you're declaring addX inside MainTable, it is not aware of anything else inseide MainTable.
One solution would be:
local MainTable
MainTable = {
...
addX = function()
MainTable.subtable.x = MainTable.subtable.x + 1
end
}
but a better way would be
local MainTable = {
subtable = {
x = 0,
y = 0
}
}
function MainTable:addX()
self.subtable.x = self.subtable.x + 1
end
-- Use it as:
MainTable:addX()

Related

Full path to the required value

How do I get the full path to the required value in the table? I want to track changes in another table through a proxy table.
I understand that I need to use metatables and __index in it. But I haven't been able to come up with a tracker yet.
Sample table structure:
Objects = {
Panel = { layer = 1, x = 600, y = 328, w = 331, h = 491;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'header' };
Window = { layer = 2, x = 400, y = 100, w = 100, h = 100;
objects = {
label = { layer = 1, x = 0, y = 0, text = 'lorem ipsum dorem' };
};
};
};
};
};
Path: Objects.Panel.objects.Window.objects.label.text
I tried to create a metatable for each of the tables and collect the result of each call to __index into a table in order to roughly understand which key and value were retrieved or changed in order to synchronize these values ​​with other tables.
This will prove itself to be horrendously slow and memory inefficient. Anyway, you were right on the track: proxy and handle __index and __newindex metamethods to your liking. This being said you also need to track the state of the proxy somehow.
You can try to hide it with some closures and upvalues but the easy way is to store the information directly in the proxy tables:
function make_tracker (o, name)
local mt = {}
mt.__index = function (proxy, key)
local path = {unpack(rawget(proxy, "__path"))} -- Stupid shallow copy
local object = rawget(proxy, "__to")
table.insert(path, key)
if type(object[key]) == "table" then
return setmetatable({__to = object[key], __path = path}, mt)
else
return table.concat(path, ".") .. " = " .. tostring(object[key])
end
end
return setmetatable({__to = o, __path = {name}}, mt)
end
__to fields indicates what proxy should point to and __path is there to cover fields we have trespassed so far. It does a shallow copy, so that one can use subproxies with local variables. name parameter is there to initialize the name of the first table, as you just simply can't know that. You use it like this:
local tObjects = make_tracker(Objects, "Objects")
local subproxy = tObjects.Panel.objects.Window
print(subproxy.objects.label.text)
print(tObjects.Panel.objects.label.text)
print(subproxy.x)
-- prints:
-- Objects.Panel.objects.Window.objects.label.text = lorem ipsum dorem
-- Objects.Panel.objects.label.text = header
-- Objects.Panel.objects.Window.x = 400
Of course, I doubt that appending the path to the original value is what you want. Modify insides of else block:
return table.concat(path, ".") .. " = " .. tostring(object[key])
according to your needs, e.g:
register_tracked_path(table.concat(path, "."))
return object[key]
If you want to handle modification of values you need to extend the metatable with similar __newindex.

Insert Additional data into Lua table at index

I have a table called "inventory", initialized like so:
inventory = {}
inventory[1] = { qty = 0 }
I want to add more data to this table, at the index 1, eg:
val = { id = "example" }
inventory[1] = inventory[1], val
Is there a way I can do this while preserving the data that is already in this table at this index?
The final result should be something like:
inventory[1] = { qty = 0, id = "example" }
But if I try to print the id after trying this code I get:
print(inventory[1].id) == Nil
inventory[1].id = "example"
or
inventory[1]["id"] = "example"
or
this other SO answer with first_table being inventory[1] and second_table being val.
FWIW, you'd need 2 variables on the left side of the expression for inventory[1] = inventory[1], val to work: a, b = x, y.
You need to take the first key in the table and use it:
local inventory = {}
inventory[1] = { qty = 0 }
local val = { id = "example" }
--
local KeyName = next(val)
inventory[1][KeyName] = val[KeyName]
print(inventory[1][KeyName])
-- or
print(inventory[1].id)

Lua - iterating through nested list

I'm going to simplify the situation as much as I can. I have the following code:
windows = { "window1", "window2" }
window1 = {
x = 100
y = 100
properties = { active = false, width = 200, height = 200 }
}
window2 = {
x = 0
y = 0
properties = { active = false, width = 200, height = 200 }
}
If I do the following, I get the correct output:
print (window1.x)
OUTPUT: 0
print (window1.properties.active)
OUTPUT: false
HOWEVER, if I iterate through the list, I get "nil" values for "l.x" and "l.properties.active":
for _,l in ipairs(windows) do
print (l)
print (l.x)
print (l.properties.active)
end
Is there a different way I need to iterate through the variables in the lists, so I can get the values?
That is not a nested table, but just a table containing strings. And, as you just saw, a string doesn't contain a value for the key "x".
You have to put the tables in a sequence:
local window1 = {...} -- first table
local window2 = {...} -- second table
local windows = {window1, window2}
for _,l in ipairs(windows) do
-- do stuff with l
end
Or, if you want to keep the list of strings and iterate over the strings, put the windows in a second table using these strings as a key.
local windowNames = { "window1", "window2" }
local windows = {}
windows.window1 = {...} -- first table
windows.window2 = {...} -- second table
for _,l in ipairs(windowNames) do
local ourWindow = windows[l]
-- do stuff with ourWindow
end

How do I make a Lua function return key,pairs to a yet unpopulated table?

I want the key pairs to be tableToPopulate.width = 30 and tableToPopulate.Height = 20
They are currently tableToPopulate[1] = 30 and tableToPopulate[2] = 20
local function X ()
code, code...
return 30,20
end
local tableToPopulate = {
x()
}
Why don't you just return a table?
local function X ()
return {width=30, height=20}
end
You could pass in the table you want the values set on, like this:
function x(tbl)
tbl.Height = 20;
tbl.Width = 30;
end
local t={}
x(t)
print(t.Height, t.Width)
although it may make more sense to use nested tables depending on how complex the structure will be of whatever is in the table.
function x(tbl)
table.insert(tbl, {Height = 20, Width = 30})
end
local t={}
x(t)
print(t[1].Height, t[1].Width)
which would be equivalent to this:
function x()
return {Height = 20, Width = 30}
end
local t = {x()}
print(t[1].Height, t[1].Width)
So really, it depends on how you want to group the data and which syntax you prefer.

How to reference lua table member from table member?

i have a table in lua:
enUS = {
LOCALE_STHOUSANDS = ",", --Thousands separator e.g. comma
patNumber = "%d+["..LOCALE_STHOUSANDS.."%d]*", --regex to find a number
["PreScanPatterns"] = {
["^("..patNumber..") Armor$"] = "ARMOR",
}
}
So you see there is a whole chain of self-references in this table:
LOCAL_STHOUSANDS
patNumber
["^("..patNumber..") Armor$"]
How can i perform self-referencing in an lua table?
What i don't want to do is have to hard-replace the values; there are hundreds of references:
enUS = {
LOCALE_STHOUSANDS = ",", --Thousands separator e.g. comma
patNumber = "%d+[,%d]*", --regex to find a number
["PreScanPatterns"] = {
["^(%d+[,%d]*) Armor$"] = "ARMOR",
}
}
How can i perform self-referencing in an lua table?
You don't.
Lua is not C. Until the table is constructed, none of the table entries exist. Because the table itself doesn't exist yet. Therefore, you can't have one entry in a table constructor reference another entry in a table that doesn't exist.
If you want to cut down on repeated typing, then you should use local variables and do/end blocks:
do
local temp_thousands_separator = ","
local temp_number_pattern = "%d+["..LOCALE_STHOUSANDS.."%d]*"
enUS = {
LOCALE_STHOUSANDS = temp_thousands_separator, --Thousands separator e.g. comma
patNumber = "%d+["..temp_thousands_separator.."%d]*", --regex to find a number
["PreScanPatterns"] = {
["^("..temp_number_pattern..") Armor$"] = "ARMOR",
}
}
end
The do/end block is there so that the temporary variables don't exist outside of the table creation code.
Alternatively, you can do the construction in stages:
enUS = {}
enUS.LOCALE_STHOUSANDS = ",", --Thousands separator e.g. comma
enUS.patNumber = "%d+["..enUS.LOCALE_STHOUSANDS.."%d]*", --regex to find a number
enUS["PreScanPatterns"] = {
["^("..enUS.patNumber..") Armor$"] = "ARMOR",
}
There's no way of doing this inside the constructor itself, but you can do it after creating the table like so:
enUS = {
LOCALE_STHOUSANDS = ","
}
enUS.patNumber = "%d+["..enUS.LOCALE_STHOUSANDS.."%d]*"
enUS.PreScanPatterns = {
["^("..enUS.patNumber..") Armor$"] = "ARMOR",
}
If you specifically need to refer to the current table, Lua provides a "self" parameter, but it's only accessible in functions.
local t = {
x = 1,
y = function(self) return self.x end
}
-- this is functionally identical to t.y
function t:z() return self.x end
-- these are identical and interchangeable
print(t:y(), t.z(t))
-- 1, 1

Resources