Correct test in .lua - lua

I'm new in lua.
Sorry for too much text, but I really need help
There is a space "company". Inside it's "information" map. Inside this map is a "job" object and an array of "users" objects. The "users" array consists of 2 objects. Each object has 4 fields.
"company": {
"company_id" : 1,
"type" : "01",
"number" : "YES",
"information":
{
"job":
{
"job_name": "N",
"job_address": 1670392687114
},
"users":
[
{
"id": 1,
"name": "Alex",
"rate": 4,
"address": "bla bla bla"
},
{
"id": 2,
"name": "Jenifer",
"rate": 5,
"address": "bla bla bla"
}
]
}
}
There is a migration for renaming fields in a space and adding new field data, provided:
local space_name = 'company'
local space = box.space[space_name]
local key_parts = space.index.primary.parts
local updated = 0
local counter = 0
local isUpdateType = {
["01"] = true,
["02"] = true,
["03"] = true,
["04"] = true,
}
for _, tuple in space:pairs() do
if tuple.information ~= nil and tuple.information.users ~= nil then
local information = tuple.information
local users = tuple.information.users
-- rename fields and assign nil to the old fields
for _, attr in pairs(users) do
attr.user_rate = attr.rate
attr.rate = nil
attr.user_address = attr.address
attr.address = nil
end
-- update data according to the condition
if isUpdateType[tuple.type] and tuple.number == "YES" then
for _, attr in pairs(users) do
attr.status = "UPDATED"
end
end
local key = {}
for _, part in ipairs(key_parts) do table.insert(key, tuple[part.fieldno]) end
space:update(key, { {'=', 'information', information} })
updated = updated + 1
end
counter = counter + 1
if counter % 1000 == 0 then
fiber.yield()
end
if counter % 10000 == 0 then
log.info('%s: %s tuples have been checked in space %s', migration, counter, space_name)
end
end log.info('%s: %s tuples have been updated from space %s', migration, updated, space_name)
Here we are at the beginning
rename fields and assign nil to the old fields
update data according to the condition: if type = "01" or "02" or "03" or "04" and number = "YES", then add a new field status = "UPDATED" to each element of the users array
I wrote a part of the test that checks if there is a "company" space and if it contains an "information" map and if it contains an array "users" in which the fields from "rate" and "address" have been renamed to the new "user_rate" and "user_address".
I'm not sure I wrote the test correctly.
Also I'm missing part of the test for the second condition of the migration: adding a new field status = "UPDATED" under a certain condition.
local space_name = 'company'
g.before_all(function()
utils.init_config_loader(helper)
utils.apply_migrations_before(helper, '005')
end)
g.after_all(function()
utils.cleanup(helper)
end)
g.check_if_exist_space = function()
local operation = 'upsert'
utils.data_into_space(helper, space_name, 4, operation)
utils.apply_migrations(helper, { '005_update_company.lua' },
{ './migrations/005_update_company.lua' })
for index = 1, 4 do
local res, err = helper.cluster.main_server.net_box:eval([[
local router_helper = require('app.utils.router_helper')
local space_name, key, shard_value = ...
return crud.get(space_name, key, { fields = {'information'}})
]], { space_name, 'company_id' .. index })
t.assert_equals(err, nil)
t.assert_equals(#res.rows, 1)
t.assert_equals(#res.rows[1], 1)
local information = res.rows[1][1]
t.assert_equals(#information, 1)
t.assert_equals(information.users[1].rate, 'user_rate' .. index)
t.assert_equals(information.users[1].address, 'user_address' .. index)
end
end
Can you help me please?

Related

How to rename fields in array in .lua

I'm new in .lua. I read the documentation, but didn't find the answer to my question.
There is a space "company".
Inside it's an "information" map.
Inside this map is a "job" object and an array of "users" objects.
The "users" array consists of 2 objects. Each object has 4 fields.
I need to rename 2 fields:
Old field names -> rate and address.
New field names -> user_rate and user_address
"company": {
"information":
{
"job":
{
"job_name": "N",
"job_address": 1670392687114,
"job_salary": 1234567890123,
"contacts": 0
},
"users":
[
{
"id": 1,
"name": "Alex",
"rate": 4,
"address": "bla bla bla"
},
{
"id": 2,
"name": "Jenifer",
"rate": 5,
"address": "bla bla bla"
}
]
}
}
My solution was the following:
for _, tuple in space:pairs() do
if tuple.params ~= nil or tuple.params.offer_params ~= nil then
local information = tuple.information or {}
local users = information.users
for _, attr in pairs(users) do
local user_rate = attr.rate
local user_address = attr.address
end
local key = {}
for _, part in ipairs(key_parts) do table.insert(key, tuple[part.fieldno]) end
space:update(key, { {'=', 'information', information} })
Here I am trying to rename rate to -> user_rate and address to -> user_address and then doing an update.
Please tell me what is wrong here.
Please help me figure it out.
for _, attr in pairs(users) do
local user_rate = attr.rate
local user_address = attr.address
end
he're you're just creating two local variables and assign a value to them. After the loop they run out of scope. So you really didn't do anything useful.
If you want to "rename" table fields you need to assign the values of those fileds to the new field names and assign nil to the old field.
for _, user in pairs(users) do
user.user_rate = user.rate
user.rate = nil
user.user_address = user.address
user.address = nil
end
For more you might want to implement a function that does that to keep your code clean.

Adding a new field to each array element in .lua

Please tell me, if there are any errors here and how to make the code more clean.
There is a space "company". Inside it's a string "type" and "information" map. Inside this map is a "job" object and an array of "users" objects. The "users" array consists of 2 objects. Each object has 4 fields.
I need to add a new field:
status = "UPDATED"
inside each object in the "users" array, under a certain condition
"company": {
"type" : "01",
"information":
{
"job":
{
"job_name": "N",
"job_address": 1670392687114,
"job_salary": 1234567890123,
"contacts": 0
},
"users":
[
{
"id": 1,
"name": "Alex",
"rate": 4,
"address": "bla bla bla"
},
{
"id": 2,
"name": "Jenifer",
"rate": 5,
"address": "bla bla bla"
}
]
}
}
My logic is next:
if tuple.type == "01" or tuple.type == "02" or tuple.type == "03" or tuple.type == "04" then
for _, attr in pairs(users) do
attr.status = "UPDATED"
end
end
Is it correct to add a new status="UPDATED" field to each object in the array "users" here?
Does this entry exist?
And yet, tell me please, can I somehow make the condition in if more beautiful? For example analog in Java list.contains()
Updated:
The final version after adding new field "status" should look like this (see image)
Is it correct to add a new status="UPDATED" field to each object in
the array "users" here? Does this entry exist?
If you want the table to have a status field with value "UPDATED" then attr.status = "UPDATED"is correct.
This only makes sense if you'll add more non-updated users to that table later though as you're updating the entire list. you could as well just mark the users table object as updated.
And yet, tell me please, can I somehow make the condition in if more
beautiful? For example analog in Java list.contains()
No but you could write your own function.
function table.contains(tbl, value)
for _, v in pairs(tbl) do
if (v == value) then return true end
end
return false
end
if table.contains({"01", "02", "03", "04"}, tuple.type) then
for _, attr in pairs(users) do
attr.status = "UPDATED"
end
end
Alternatively you could use a lookup table
local isUpdateType = {
["01"] = true,
["02"] = true,
["03"] = true,
["04"] = true,
}
and later
if isUpdateType[tuple.type] then end
How to solve this depends very much on what you consider "beautiful" here.

Trying to insert table into table without creating a new table when saving to a file

I am attempting to add a "recipe" (Table) into a file for future look up.
My issue is that when I upload the data into the file, it creates a new table and does not insert into the "Main" table as a nested table. This causes issues when trying to search for a "recipe" within the main "recipe book".
The following is the code for this section of the project. The issue I believe is near the uploading of the data to a file but I am stumped. Can someone explain what the heck I am doing wrong?
When I run the code a second time it adds in the information into the main table without issue, but it adds it under the original table.
When I run the code any more times it just adds in a new table.
Code:
--recursive search in--recursive search into tables
local Deep = require "DeepPrint"
--Working from the following info
--http://www.computercraft.info/forums2/index.php?/topic/23076-crafting-api-learn-recipes/
--the solution found: https://stackoverflow.com/questions/19299162/lua-table-expected-got-nilo
--variables--
--data holding place for recipes
Recipes = {}
--Crafting Grid
grid = {1,2,3,5,6,7,9,10,11};
--check for recipe book, if exists then read the data
local readRecipe = fs.open("recipeBook.lua","r")
if readRecipe == nil then
print("recipe book not found")
else
recipeList = readRecipe.readAll()
readRecipe.close()
ok, Recipes = pcall(textutils.unserialize, recipeList)
if not ok then
return false, recipeList
end
if type(Recipes) ~= "table" then
print("Main table not found, creating base table")
Recipes = {}
end
end
if readRecipe == nil then
print("recipe book not found")
else
recipeList = readRecipe.readAll()
readRecipe.close()
ok, Recipes = pcall(textutils.unserialize, recipeList)
if not ok then
return false, recipeList
end
if type(Recipes) ~= "table" then
print("Main table not found, creating base table")
Recipes = {}
end
end
--get recipe location and details to push into recipeBook.lua
itemLayout = {}
for key,value in pairs(grid) do
turtle.select(value)
if turtle.getItemDetail() then
details = turtle.getItemDetail()
table.insert(itemLayout,{
name = details.name,
count = details.count,
location = value
})
end
end
--craft item to get the name of crafted item
turtle.select(16)
turtle.craft()
itemMade = turtle.getItemDetail()
--check if table exists and if not creates a table
if not Recipes[itemMade.name] then
print("didnt find the table for item, creating new table")
Recipes[itemMade.name] = {}
else
print("found recipe")
end
--add in info into main product table
Recipes[itemMade.name]["components"] = itemLayout
Recipes[itemMade.name]["quantityMade"] = itemMade.count
--add recipe to list
local ok, str = pcall(textutils.serialize, Recipes)
if not ok then
return false, str
else
book = fs.open("recipeBook.lua","a")
book.writeLine(str)
book.close()
end
Results in the following type of table
{
[ "minecraft:crafting_table" ] = {
components = {
{
location = 1,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 2,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 5,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 6,
name = "minecraft:oak_planks",
count = 1,
},
},
quantityMade = 1,
},
}
{
[ "minecraft:oak_button" ] = {
components = {
{
location = 1,
name = "minecraft:oak_planks",
count = 1,
},
},
quantityMade = 1,
},
[ "minecraft:crafting_table" ] = {
components = {
{
location = 1,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 2,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 5,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 6,
name = "minecraft:oak_planks",
count = 1,
},
},
quantityMade = 1,
},
}
{
[ "minecraft:oak_button" ] = {
components = {
{
location = 1,
name = "minecraft:oak_planks",
count = 1,
},
},
quantityMade = 1,
},
}
{
[ "minecraft:oak_pressure_plate" ] = {
components = {
{
location = 1,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 2,
name = "minecraft:oak_planks",
count = 1,
},
},
quantityMade = 1,
},
}
{
[ "minecraft:crafting_table" ] = {
components = {
{
location = 1,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 2,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 5,
name = "minecraft:oak_planks",
count = 1,
},
{
location = 6,
name = "minecraft:oak_planks",
count = 1,
},
},
quantityMade = 1,
},
}
I found the issue to this.
is due to the fact I had it appending to the file and not writing when uploading the information back to the file.
2)The second was the pointless array at the beginning with the variable Recipes, before even starting to gather the info from the file. This caused the upload to have another nest that wasn't needed

How to implement the exercise 15.5 in pil4?

I am working on this exercise in pil4.
Exercise 15.5:
The approach of avoiding constructors when saving tables with cycles is too radical. It is
possible to save the table in a more pleasant format using constructors for the simple case, and to use
assignments later only to fix sharing and loops. Reimplement the function save (Figure 15.3, “Saving
tables with cycles”) using this approach. Add to it all the goodies that you have implemented in the previous
exercises (indentation, record syntax, and list syntax).
I have tried this with the code below, but it seems not to work on the nested table with a string key.
local function basicSerialize(o)
-- number or string
return string.format("%q",o)
end
local function save(name,value,saved,indentation,isArray)
indentation = indentation or 0
saved = saved or {}
local t = type(value)
local space = string.rep(" ",indentation + 2)
local space2 = string.rep(" ",indentation + 4)
if not isArray then io.write(name," = ") end
if t == "number" or t == "string" or t == "boolean" or t == "nil" then
io.write(basicSerialize(value),"\n")
elseif t == "table" then
if saved[value] then
io.write(saved[value],"\n")
else
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("{\n")
end
local indexes = {}
for i = 1,#value do
if type(value[i]) ~= "table" then
io.write(space2)
io.write(basicSerialize(value[i]))
else
local fname = string.format("%s[%s]",name,i)
save(fname,value[i],saved,indentation + 2,true)
end
io.write(",\n")
indexes[i] = true
end
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("}\n")
else
io.write("{}\n")
end
saved[value] = name
for k,v in pairs(value) do
if not indexes[k] then
k = basicSerialize(k)
local fname = string.format("%s[%s]",name,k)
save(fname,v,saved,indentation + 2)
io.write("\n")
end
end
end
else
error("cannot save a " .. t)
end
end
local a = { 1,2,3, {"one","Two"} ,5, {4,b = 4,5,6} ,a = "ddd"}
local b = { k = a[4]}
local t = {}
save("a",a,t)
save("b",b,t)
print()
And I got the wrong ouput.
a = {
1,
2,
3,
{
"one",
"Two",
}
,
5,
{
4,
5,
6,
}
a[6]["b"] = 4
,
}
a["a"] = "ddd"
b = {}
b["k"] = a[4]
How could I make the text ' a[6]["b"] = 4 ' jump out of the table constructor?

Lua iterator to array

In Lua parlance, is there any syntactic sugar for turning an iterator function into an array (repeated invocations with results stored in ascending indices), perhaps something in the standard library ?
I'm tokenizing a string belonging to a protocol and need to to have positional access to elements at the start of the string, and the end of the string is a variant collection.
The code (specific to my use-case) is as follows, I find it hard to believe that it isn't in the standard library :d
local array_tokenise = function (line)
local i = 1;
local array = {};
for item in string.gmatch(line,"%w+") do
array[i] = item;
i = i +1
end
return array
end
There's no standard library function for it. But really, it's pretty trivial to write:
function BuildArray(...)
local arr = {}
for v in ... do
arr[#arr + 1] = v
end
return arr
end
local myArr = BuildArray(<iterator function call>)
This will only work if your iterator function returns single elements. If it returns multiple elements, you'd have to do something different.
As Nicol Bolas said, there is no standard library function that performs the action you desire.
Here is a utility function that extends the table library:
function table.build(build_fn, iterator_fn, state, ...)
build_fn = (
build_fn
or function(arg)
return arg
end
)
local res, res_i = {}, 1
local vars = {...}
while true do
vars = {iterator_fn(state, vars[1])}
if vars[1] == nil then break end
--build_fn(unpack(vars)) -- see https://web.archive.org/web/20120708033619/http://trac.caspring.org/wiki/LuaPerformance : TEST 3
res[res_i] = build_fn(vars)
res_i = res_i+1
end
return res
end
Here is some example code demonstrating usage:
require"stringify"
local t1 = {4, 5, 6, {"crazy cake!"}}
local t2 = {a = "x", b = "y", c = "z"}
print(stringify(table.build(nil, pairs(t1))))
print(stringify(table.build(nil, pairs(t2))))
print(stringify(table.build(
function(arg) -- arg[1] = k, arg[2] = v
return tostring(arg[1]).." = "..tostring(arg[2])
end
, pairs(t1)
)))
local poetry = [[
Roses are red, violets are blue.
I like trains, and so do you!
By the way, oranges are orange.
Also! Geez, I almost forgot...
Lemons are yellow.
]]
print(stringify(table.build(
function(arg) -- arg[1] == plant, arg[2] == colour
return (
string.upper(string.sub(arg[1], 1, 1))..string.lower(string.sub(arg[1], 2))
.. " is "
.. string.upper(arg[2]).."!"
)
end
, string.gmatch(poetry, "(%a+)s are (%a+)")
)))
Output:
{
[1] = {
[1] = 1,
[2] = 4,
},
[2] = {
[1] = 2,
[2] = 5,
},
[3] = {
[1] = 3,
[2] = 6,
},
[4] = {
[1] = 4,
[2] = {
[1] = "crazy cake!",
},
},
}
{
[1] = {
[1] = "a",
[2] = "x",
},
[2] = {
[1] = "c",
[2] = "z",
},
[3] = {
[1] = "b",
[2] = "y",
},
}
{
[1] = "1 = 4",
[2] = "2 = 5",
[3] = "3 = 6",
[4] = "4 = table: 00450BE8",
}
{
[1] = "Rose is RED!",
[2] = "Violet is BLUE!",
[3] = "Orange is ORANGE!",
[4] = "Lemon is YELLOW!",
}
stringify.lua can be found here
if you are just looking to auto-increment the table key for each data element, you can use table.insert(collection, item) - this will append the item to the collection and sets the key to the collection count + 1

Resources