All the examples for splitting strings generate arrays. I want the following
Given string like x.y.z e.g. storage.clusters.us-la-1
How do I generate a table from that resembling
x = {
y = {
z = {
}
}
}
Below is a function that should do what you want.
function gen_table(str, existing)
local root = existing or {}
local tbl = root
for p in string.gmatch(str, "[^.]+") do
local new = tbl[p] or {}
tbl[p] = new
tbl = new
end
return root
end
Usage:
local t = gen_table("x.y.z")
local u = gen_table("x.y.w", t)
t.x.y.z.field = "test1"
t.x.y.w.field = "test2"
Related
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.
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)
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
Is there a way that I can convert a hierarchy string into table form?
Suppose the input is A.B.C.D
ouput should be a table which traverses above input:
A = {}
A.B = {}
A.B.C = {}
A.B.C.D = {}
Thanks.
The obvious solution would be to parse the string up and construct the hierarchy table from that. But a more clever solution is to let lua do it for you. With a bit of metamagic and function environment manipulation this can be done:
dump = require 'pl.pretty'.dump -- convenient table dumper from penlight
function createtable(str)
local env_mt = {}
env_mt.__index = function(t, k)
rawset(t, k, setmetatable({}, env_mt))
return rawget(t, k)
end
local env = setmetatable({}, env_mt)
local f = loadstring("return "..str)
setfenv(f, env)
f()
return env
end
dump( createtable "A.B.C.D" )
this outputs:
{
A = {
B = {
C = {
D = {
}
}
}
}
}
#greatwolf's answer is right but I prefer the more straightforward approach of "parsing" the string and constructing the table. Less magic, and you do not execute a function loaded from a (possibly) user-defined string, which would be a security issue.
local createtable = function(str)
local top = {}
local cur = top
for i in str:gmatch("[^.]+") do
cur[i] = {}
cur = cur[i]
end
return top
end
(require "pl.pretty").dump(createtable("A.B.C.D"))
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