Dynamically building subtables in a table - lua

I'm trying to figure out how to dynamically build a series of sub-tables inside a lua table. For example
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i = 1, 2 do
oTable[i] = {name = name[i], "x" = i + 2, "y" = i + 1}
end
return oTable
end
expected output:
oTable = {
{name = "P1", "x"=3, "y"=2},
{name = "P2", "x"=4, "y"=3}
}
Which obviously doesn't work, but you get the idea of what I'm trying to do. This is a rather simple task but in LUA 5.3 this is proving to be difficult. I cannot find a good example of building a table in this manner. Any solutions or other ideas would be appreciated. In Python I would use a class or a simple dictionary.

Your problem is that you quote the string indices. The generic syntax for declaring a table key inside the table constructor is [<key>] = <value>, for example, [20] = 20 or ["x"] = i + 2.
A shorthand for ["<key>"] = <value>, that is, for string indices that are valid variable names, you can write <key> = <value>, for example x = i + 2.
In your code you use a mix of both and write { ..., "x" = i + 2, ... }. A quick google search shows me that in Python, which you mention, you quote string keys in dictionaries, so you probably mixed that up with Lua?
EDIT: I noticed this a bit late, but you can also use ipairs to iterate the first table and table.insert to insert values:
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i,name in ipairs(name) do
table.insert(oTable, {name = name, "x" = i + 2, "y" = i + 1})
end
return oTable
end

Use
oTable[i] = {name = name[i], x = i + 2, y = i + 1}

DarkWiiPlayers & lhf's answers are the proper way.
But here is how you can fix your current code if you intend to use a string as a key
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i = 1, 2 do
oTable[i] = {name = name[i], ["x"] = i + 2, ["y"] = i + 1}
end
return oTable
end
Output
{
[1] = { ['name'] = 'P1', ['x'] = 3, ['y'] = 2},
[2] = { ['name'] = 'P2', ['x'] = 4, ['y'] = 3}
}

Related

Access a specific table depending on a variable in lua

Hello I have a little problem with lua. I want to access one of the tables containing the route information depending on the route variable defined on the start of the script. But everytime I run the program I always get nil as an answer. Does anybody know how to fix that?
route = 1
local eastCityRoute = {
{name = "ecr-stop1"},
{name = "ecr-stop2"},
{name = "ecr-stop3"},
}
local westCityRoute = {
{name = "wcr-stop1"},
{name = "wcr-stop2"},
{name = "wcr-stop3"},
}
routes = {}
routes[1] = eastCityRoute
routes[2] = westCityRoute
print(routes[route][name])
local eastCityRoute = {
{name = "ecr-stop1"},
{name = "ecr-stop2"},
{name = "ecr-stop3"},
}
is short for
local eastCityRoute = {
[1] = {["name"] = "ecr-stop1"},
[2] = {["name"] = "ecr-stop2"},
[3] = {["name"] = "ecr-stop3"},
{
Let's look at your remaining code:
You create a global table routes and store two tables eastCityRoute and westCityRoute in it.
routes = {}
routes[1] = eastCityRoute
routes[2] = westCityRoute
Now you try this:
print(routes[route][name])
route is 1 so routes[route] is equivalent to routes[1] which resolves to eastCityRoute.
But eastCityRoute[name] is equal to eastCityRoute[nil]. As eastCityRoutes does not have a metatable with a proper __index metavalue eastCityRoute[nil] resolves to nil.
eastCityRoute[name] is not the same as eastCityRoute["name"]!!!
In one case you index with a variable and in the other case you index with a literal string.
Please read the Lua Reference Manual.

Print the Lua table and connect the strings

I want to print the table, more complex. like this:
I don't know how to check each value in the table that equals and connects string like this (value1, value2, value3, and value4) Before it ends, it must end with and
Table:
table = {
{amount = 1, items = "item1"},
{amount = 1, items = "item2"},
{amount = 1, items = "item3"},
{amount = 2, items = "item4"},
{amount = 3, items = "item5"},
}
P.S I need to sort them too.
table.sort(table, function(a,b) return a.amount < b.amount end)
but I am still stuck with connect string that I mentioned above.
I want it to output like this:
3x item5
2x item4
1x item1, item2, and item3
Thanks for the answers, I newbie to Lua, and English is not my native language. Sorry for the incorrect grammar and words.
the solution is something like this, only without the output of and between item2, and item3 :
local t = {
{amount = 1, items = "item1"},
{amount = 1, items = "item2"},
{amount = 1, items = "item3"},
{amount = 2, items = "item4"},
{amount = 3, items = "item5"},
}
local res = {}
for k,v in pairs(t) do
res[v.amount] = res[v.amount] and (res[v.amount] .. ', ' .. v.items) or v.items -- use ternary operator
end
for k,v in pairs(res) do
v = v:gsub("(.*), (.-)","%1 and %2")
print(k,v)
end

Sorting an array of tables

This may be easier than I'm making it on myself. I am relatively new to Lua, but experienced in other languages.
I have a table that looks like this:
local state = {}
state[1] = {
show = true,
changed = true,
progressType = "static",
value = 0,
total = 9,
name = "nine",
}
state[2] = {
show = true,
changed = true,
progressType = "static",
value = 0,
total = 7,
name = "seven",
}
state[3] = {
show = true,
changed = true,
progressType = "static",
value = 0,
total = 8,
name = "eight",
}
state[4] = {
show = true,
changed = true,
progressType = "static",
value = 0,
total = 6,
name = "six",
}
What I need to do is sort each table[] entry based on the value of table.value5. I can't find any functions in the docs that expressely say they do more than just a basic table.sort so I find myself a bit stuck. Do I need to manually sort by iterating through and creating a new table with the sorted data?
I can't find any functions in the docs that expressely say they do more than just a basic table.sort so I find myself a bit stuck.
I might be misunderstanding your issue, but table.sort is exactly what you need in this case:
local state = {}
state[1] = {
total = 9,
name = "nine",
}
state[2] = {
total = 7,
name = "seven",
}
state[3] = {
total = 8,
name = "eight",
}
state[4] = {
total = 6,
name = "six",
}
-- Use table.sort with a custom anonymous function
-- to specify how to compare the nested tables.
table.sort(state, function (a, b)
return a.total < b.total
end)
for i=1, #state do
print(i, state[i].name)
end
With table.sort you can provide an optional custom function, which can be as easy or complicated as you want it to be. In this case (as per your question) it is enough to simply compare the total values of your table.

How to return a table with string keyed key value pairs from a lua script to node js?

I have a lua script. I am running from node.js and my script is returning a lua table with key value pairs in it like { '1': 15, '2': 35 }. In node I am getting an empty array. However if I return arrayName['1']. It gives me proper value 15. What am I doing wrong here. I am using node_redis client for node to redis. Diffrent files are given below.
Lua Script - groupBy.lua
local db = "";
local table = "students";
local groupBy = "class";
local aggregate = "marks";
local a = {};
local marksGroupedByClass = {};
for i = 1, 4 do
local dataArr = redis.call("hgetall", db..table..":"..i);
local dataObj = {};
for i = 1, #dataArr, 2 do
dataObj[dataArr[i]] = dataArr[i+1];
end
if a[dataObj["class"]] then
a[dataObj["class"]] = a[dataObj["class"]] + dataObj["marks"]
else
a[dataObj["class"]] = dataObj["marks"]
end
end
return a['2']; -- This returns proper value 35
-- return a; This returns empty array to node
node file - luaExec.js
let fs = require('fs');
let redis = require('redis');
let client = redis.createClient();
client.on("error", cb);
function cb(err) {
console.log("Error " + err);
}
client.eval(fs.readFileSync('groupBy.lua'), 0, (err, replies) => {
if(err) cb(err);
console.log(replies);
})
client.quit();
I saved my data with this script - saveStudentData.lua
redis.call("hmset","students:1", "name", "A", "class", 1, "marks", 5 );
redis.call("hmset","students:2", "name", "B", "class", 1, "marks", 10 );
redis.call("hmset","students:3", "name", "C", "class", 2, "marks", 15 );
redis.call("hmset","students:4", "name", "D", "class", 2, "marks", 20 );
TL;DR you can't.
Redis has no support for a key-value type of response (although there are talks of changing that in the next version of the protocol RESP3).
Thus, returning an associative array from Lua (a table with kv pairs) results in an empty array as you've found out. What you'll need to do is flatten it, e.g.:
...
local reply = {} for k, v in pairs(a) do
table.insert(reply, k)
table.insert(reply, v)
end
return reply
Or simply prepare a flattened in the preceding loop. Lastly, your client will have to be aware of this format and recompose the object from it, should that be needed.

Emulate string to label dict

Since Bazel does not provide a way to map labels to strings, I am wondering how to work around this via Skylark.
Following my partial horrible "workaround".
First the statics:
_INDEX_COUNT = 50
def _build_label_mapping():
lmap = {}
for i in range(_INDEX_COUNT):
lmap ["map_name%s" % i] = attr.string()
lmap ["map_label%s" % i] = attr.label(allow_files = True)
return lmap
_LABEL_MAPPING = _build_label_mapping()
And in the implementation:
item_pairs = {}
for i in range(_INDEX_COUNT):
id = getattr(ctx.attr, "map_name%s" % i)
if not id:
continue
mapl = getattr(ctx.attr, "map_label%s" % i)
if len(mapl.files):
item_pairs[id] = list(mapl.files)[0].path
else:
item_pairs[id] = ""
if item_pairs:
arguments += [
"--map", str(item_pairs), # Pass json data
]
And then the rule:
_foo = rule(
implementation = _impl,
attrs = dict({
"srcs": attr.label_list(allow_files = True, mandatory = True),
}.items() + _LABEL_MAPPING.items()),
Which needs to be wrapped like:
def foo(map={}, **kwargs):
map_args = {}
# TODO: Check whether order of items is defined
for i, item in enumerate(textures.items()):
key, value = item
map_args["map_name%s" % i] = key
map_args["map_label%s" % i] = value
return _foo(
**dict(map_args.items() + kwargs.items())
)
Is there a better way of doing that in Skylark?
To rephrase your question, you want to create a rule attribute mapping from string to label?
This is currently not supported (see list of attributes), but you can file a feature request for this.
Do you think using "label_keyed_string_dict" is a reasonable workaround? (it won't work if you have duplicated keys)

Resources