I'd like to maintain the order of a table when updating table values in Lua.
Example
tbl = {
messageId = 0,
timestamp = currentTime,
responseStatus = {
status = "FAILED",
errorCode = "599",
errorMessage = "problem"
}
}
meaning tbl.messageId = 12345 leaves the elements ordered
Like #moteus said, your premise is incorrect: non-numeric entries in Lua tables are not sorted. The order, in which they are defined won't, in general, be the same order as that in which they will be read (e.g., pairs will iterate over those entries in an arbitrary order). Assigning a new value will not affect this in any way.
I suppose you can use table.sort, there is a simple example:
local tbl = {
messageId = 0,
timestamp = currentTime,
responseStatus = {
status = "FAILED",
errorCode = "599",
errorMessage = "problem"
}
}
function fnCompare (e1, e2)
-- you should promise e1 and e2 is tbl struct
-- you can check e1 and e2 first by yourself
return e1.messageId < e2.messageId;
end
-- test
local tbAll = {}
tbl.messageId = 3;
table.insert(tbAll, tbl);
-- add a another
table.insert(tbAll, {messageId = 1});
table.sort(tbAll, fnCompare);
for k, v in ipairs(tbAll) do
print(v.messageId); -- result: 1 3
end
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 have the following snip of dxl code,
I would like to copy the object ID with the filter F3 is on. :
I dont know what I am doing wrong it gives me (ID) of all the object.
string Id
int x=0;
int y=0;
Id = o."SourceID"
Filter f0 = hasNoLinks(linkFilterIncoming, "*")
Filter f1=attribute "_TraceTo" == "System"
Filter f2 = attribute "Object Type" == "requirement"
Filter f3 = f1&&f2&&f0
addFilter(m,f3,x,y)
print x ":\t" fullName(module(m)) "\n"
wOutKLHUntraced << Id "\t" fullName(module(m)) "\n"
First, you need to add the statement filtering on after adding the filter, so that the filter is applied. Then the filtered objects will be the only ones visible.
Then, you set "Id" way too early in the script. At line 4, "o" is set to
some object, I don't know which one, but certainly not the result of
your filter. Instead, after the statement filtering on, add statements
Object o = first m // the first object that is now visible
Id = o."SourceID"
My Script is running good, but gives different results : as I am running this script in a for loop for around 30 module :
Am I am setting somewhere wrong filters ?
Stream TbdUntraced;
string s
string d
Object o
string trac
int numReqs = 0;
string IdNum
string untraced
int x=0;
int y=0;
int a =0;
for o in m do
{
ensureInLinkedModulesLoaded(o,S_SATISFIES );
s = o."Object Type"
string Id
string Topic
Topic = o."_Topic"
numReqs++;
Filter f0 = hasNoLinks(linkFilterIncoming, "*")
Filter f1 = contains(attribute "_TraceTo", "TBD", false)
Filter f2 = attribute "Object Type" == "requirement"
Filter f3 = attribute "MMS5-Autoliv_Supplier_Status" == "agreed"
Filter f4 = attribute "MMS5-Autoliv_Supplier_Status" == "partly agreed"
Filter f7 = f0&&f2&&(f3||f4)&&f1
addFilter(m,f7,x,y)
filtering on
d = o."MMS5-Autoliv_OEM_Status"
Id = o."SourceID"
Topic = o."_Topic"
print x ":\t" name(module(m)) "\n"
TbdUntraced << Id "\t" Topic "\t"name(module(m)) "\n"
}
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
I'm writing a program with lua. I have data that organized in the following way:
t= {
i1 = {
p1 = { value = "i1p1" },
p2 = { value = "i1p2" },
-- etc
pm = { value = "i1pm" }
},
i2 = {
p1 = { value = "i2p1" },
p2 = { value = "i2p2" },
-- etc
pm = { value = "i2pm" }
},
im = {
p1 = { value = "imp1" },
p2 = { value = "imp2" },
-- etc
pm = { value = "impm" }
}
} --(inner tables)
In another way each group of data is indexed by two variables i&p,I am sure that the data is kept correctly but I want a way to print the data from their tables because I won't know the values of i and p to iterate over them or even the numbers n & m any body know how to do this with lua?
If you know the depth of your nested (inner) tables, you can iterate explicitly to that depth:
for k1,v1 in pairs(t) do
for k2,v2 in pairs(v1) do
for k3, v3 in pairs(v2) do
print(k3, ":", v3)
end
end
end
Alternatively, you can recursively iterate into your nested structure:
function print_tbl(tbl)
if type(tbl) == "table" then
for _,v in pairs(tbl) do
print_tbl(v)
end
else
print(tbl)
end
end
print_tbl(t)
This is just an example. If your tables contain functions, contains userdata, or your nesting has cycles, you'll need a different approach. Take a look at table serialization on the Lua user wiki. Serialization requires sensible handling of tables with nesting, functions, cycles, etc. You may be able to use lessons learned on your data.