Tarantool: limit/offset in index.indexName:pairs call - lua

I need to fetch some records from space users.
This space has a secondary index category_status_rating.
I need select users with category=1, status=1, rating<=123456789:
for _, user in box.space.users.index.category_status_rating:pairs({ 1, 1, 123456789 }, { limit = 20, offset = 5, iterator = box.index.LE }) do
if user[categoryIdx] ~= 1 or user[statusIdx] ~= 1 then break end
table.insert(users, user)
end
As I know, iteration with indexName:pairs does not support limit and I can just user my own counter. But what about offset? Can I use this param and start from "page" I need? Or will I iterate without any offset and pass useless records (about 100000) and start to table.insert(users, user) when my "page" starts?
Thanks!

Instead of using offset, you can save your position (that will be last checked tuple) if you really need it.
e.g:
local last = 123456789
for i = 1, 2 do
local count = 0
for _, user in box.space.users.index.category_status_rating:pairs({1, 1, last}, { iterator = box.index.LE }) do
if user[categoryIdx] ~= 1 or user[statusIdx] ~= 1 or count > 20 then
break
end
table.insert(users, user)
last = user[LAST_INDEX_FIELD]
count = count + 1
end
-- process your tuples
end
or, using luafun (where drop_n is analog of limit, and saving into last is analog of offset):
local last = 123456789
for i = 1, 2 do
local users = box.space.users.index.category_status_rating:pairs({1, 1, last}, { iterator = box.index.LE }):take_n(20):map(function(user)
last = user[LAST_INDEX_FIELD]
return user
end):totable()
-- process your tuples
end
Documentation on LuaFun, which is embedded into Tarantool.

Related

[FIVEM]/LUA. How would i compare two player id's here

Ive been racking brains all day tryna fix this and i am new with lua and cant come up with a solution to this problem even though im rather close.
SO
I have a drift leader board in my server. The problem is the script doesnt check for the players [id] in the table. So if player A has a score of 10 and Player B has a score of 5. Player A can take the 2nd position on the leaderboard (table) by getting a score of any int between. so lets say 7.
LEADERBOARD_CLEAR_TIME = 15 -- THIS IS IN MINUTES
local Players = {}
local Leaderboards = {}
RegisterCommand('cleardriftscores', function(source)
if IsPlayerAceAllowed(source, 'drift') then --Ace permissions
clearLeaderboard()
end
end)
RegisterNetEvent("SaveScore")
AddEventHandler("SaveScore", function(client, data)
local identifier = (GetPlayerIdentifier(source, 0))
local playerName = GetPlayerName(source)
if Players[identifier] ~= nil then
if Players[identifier].pb < data.curScore then
-- Personal Best Beat
local oldScore = Players[identifier].pb
Players[identifier] = { pb = data.curScore }
chatMessage(source, string.format("Congrats! You have just beaten your personal best drift chain score of ^2%s^0 with ^2%s^0!", oldScore, data.curScore))
end
else
Players[identifier] = { pb = data.curScore }
end
if #Leaderboards == 0 then
table.insert(Leaderboards, {score = data.curScore, name = playerName, id = identifier})
chatMessage(-1, string.format("^2%s^0 has started off the leaderboard with a score of ^2%s^0!", playerName, data.curScore))
end
for k, v in ipairs(Leaderboards) do
if v.score < data.curScore and checkLeaderboard(identifier) then
-- Outpassed Someone
table.insert(Leaderboards, { score = data.curScore, name = playerName, id = identifier } )
chatMessage(-1, string.format("^2%s^0 has beaten ^2%s's^0 score of ^2%s^0 with ^2%s^0! They are in ^2%s^0 place", playerName, v.name, v.score, data.curScore, GetPlacement(k)))
break
end
end
table.sort(Leaderboards, compare) --Currently bugged, when player 1 is in first and player 2 is in second. Player 1 can take second by getting a score inbetween table entry 1 & 2
end)
function chatMessage(target, msg)
TriggerClientEvent('chat:addMessage', target or -1, {
color = { 255, 0, 0},
multiline = true,
args = {"[Drift] ", msg}
})
end
function checkLeaderboard(identifier)
for k, v in ipairs(Leaderboards) do
if v.id == identifier then
Leaderboards[k] = nil
end
end
return true
end
function compare(a, b) --actual compare funtion. Need to check for player id's somehow...
if a ~= nil and b ~= nil then
return a.score > b.score --I tried adding another compare here but that didnt work.
end
end
function GetPlacement(number) --This doesnt work very well. (12nd place??)
lastDigit = number % 10
local placement = 'th'
if lastDigit == 1 then
placement = 'st'
elseif lastDigit == 2 then
placement = 'nd'
elseif lastDigit == 3 then
placement = 'rd'
end
return number .. placement
end
function clearLeaderboard()
Leaderboards = {}
chatMessage(-1, "The Drift leaderboard has been cleared!")
end
Citizen.CreateThread(function() --Code for timer to reset leaderboard
while true do
while #Leaderboards == 0 do
Citizen.Wait(0)
end
Citizen.Wait((LEADERBOARD_CLEAR_TIME * 1000 * 60) - (60 * 1000 * 5))
chatMessage(-1, "The Drift Leaderboard is clearing in 5 minutes!")
Citizen.Wait(1000 * 60 * 3)
chatMessage(-1, "The Drift Leaderboard is clearing in 2 minutes!")
Citizen.Wait(1000 * 60 * 1)
chatMessage(-1, "The Drift Leaderboard is clearing in 1 minute!")
Citizen.Wait(1000 * 60 * 1)
clearLeaderboard()
end
end)
The problem here is the code that adds to the leaderboards.
If a player is only allowed to be on a leaderboard once, you should check for that!
Here's how I'd do it:
If the player has beaten his/her personal best then attempt to add his new score to the leaderboards.
Start from the lowest score on the leaderboard. For each score:
If the score owner is equal to the player, delete this score (it's outdated).
If the personal best is smaller than the currently inspected score, insert the personal best one below the current inspected score.
Doing so there won't be any duplicates.
Btw, compare function is just fine ;)
But, just a friendly reminder:
a ~= nil and b ~= nil
--This is simpler and faster!
a and b
Remember that nil in Lua evaluates to false. Therefore, if you need to check if something holds value just:
if a then --This checks if a isn't false or nil
--your code goes here
end
And also that line is not really useful in your comparing function.
Hope that helps!

How to select multiple random elements from a LUA table?

I'm trying to figure out how to randomly select multiple entries in a table.
This is what I have currently
local letters = {"w", "x", "y", "z"}
function randomletterselect()
local randomletters = {}
for i,v in pairs(letters) do
table.insert(randomletters,i)
end
local letter = letters[randomletters[math.random(#randomletters)]]
-- set multiple selected letters to the new table?
end
randomletterselect()
this code here works for selecting ONE random element(letter) from the table. essentially, when I run this, I want it to be multiple random letters selected. for example, one time it may select x,y another time it may be x,y,z.
The closest thing I found was honestly just what I had figured out, in this post Randomly select a key from a table in Lua
You can do...
randomletterselect=function()
local chars={}
for i=65,math.random(65,90) do
table.insert(chars,string.char(math.random(65,90)):lower())
end
print(table.concat(chars,','))
return chars
end
randomletterselect()
...for lowercase letters.
And this version returns a table without doubles...
randomletterselect=function()
local chars=setmetatable({},{__name='randomletters'})
for i=65,90 do
table.insert(chars,string.char(i):lower())
end
for i=1,math.random(#chars) do
table.remove(chars,math.random(#chars))
end
print(#chars,table.concat(chars,','))
return chars
end
local tbl = {'a', 'b', 'c', 'd'} -- your characters here
local MIN_NUM, MAX_NUM = 1, 2 ^ #tbl - 1
-- if the length of the table has changed, MAX_NUM gonna change too
math.randomseed(os.time())
local randomNum = math.random(MIN_NUM, MAX_NUM)
local function getRandomElements(num)
local rsl = {}
local cnt = 1
while num > 0 do
local sign = num % 2 -- to binary
-- if the num is 0b1010, sign will be 0101
num = (num - sign)/2
if sign == 1 then
local idx = #rsl + 1
-- use tbl[#tbl + 1] to expand the table (array)
rsl[idx] = tbl[cnt]
end
cnt = cnt + 1
end
return table.concat(rsl)
end
print(getRandomElements(randomNum))
I have another method to solve the problem, try.
MIN_NUM should be 1 cuz when its 0, getRandomElements will return an empty string

How to get the size of an array in lua

I'm trying get the size of an array and loop in it. I tried use the getn function, but it didn't work.
Here is my code:
results =
{
address= "Address 1",
type_address= "RESIDENCIAL",
phone= "654620460",
email= "email1#email.com"
},
{
address= "Address 2",
type_address= "COMERCIAL",
phone= "604654650",
email= "email1#email.com"
}
for i = 0, table.getn(results), 1 do
if results[i].type_address == "RESIDENCIAL" then
phone = results[i].phone
email = results[i].email
break
else
phone = results[1].phone
email = results[1].email
end
end
print (phone)
print (email)
To get the size of the table use #tbl for arrays.
You forgot to wrap items into {}. For now you assigned results to table with Address 1, table with Address 2 is ignored because you didn't assign it to anything (due to mistake)
Wrap it like this:
results = {
-- items here
}
Quick note: table.getn is deprecated and identical to #tbl, you can also use
for k,v in ipairs(results) do.
Third parameter of for statement is optional and defaults to 1.
for i = 0, #results do
if results[i].type_address == "RESIDENCIAL" then
-- etc
end
-- or
for k, v in ipairs(results) do
if v.type_address == "RESIDENCIAL" then
-- etc
end
I use...
function(len) local incr=0 for _ in pairs(len) do incr=incr+1 end return incr end
...as a metamethod __index.len for table key counting. Then...
> test_table={'1',two='2',pi=math.pi,popen=io.popen}
> setmetatable(test_table,{__index={len=function(len) local incr=0 for _ in pairs(len) do incr=incr+1 end return incr end}})
table: 0x565aa850
> test_table:len()
4
...it count mixed numbered and named keys correctly. Where...
> #test_table
1
...doesnt.

Can't figure out this table arrangement

--The view of the table
local originalStats = {
Info = {Visit = false, Name = "None", Characters = 1},
Stats = {Levels = 0, XP = 0, XP2 = 75, Silver = 95},
Inventory = {
Hats = {"NoobHat"},
Robes = {"NoobRobe"},
Boots = {"NoobBoot"},
Weapons = {"NoobSword"}
}
}
local tempData = {}
--The arrangement here
function Module:ReadAll(player)
for k,v in pairs(tempData[player]) do
if type(v) == 'table' then
for k2, v2 in pairs(v) do
print(k2) print(v2)
if type(v2) == 'table' then
for k3, v3 in pairs(v2) do
print(k3) print(v3)
end
else
print(k2) print(v2)
end
end
else
print(k) print(v)
end
end
end
I'm sorry, but I can't seem to figure out how to arrange this 'ReadAll' function to where It'll show all the correct stats in the right orders.
The output is something like this:
Boots
table: 1A73CF10
1
NoobBoot
Weapons
table: 1A7427F0
1
NoobSword
Robes
table: 1A743D50
1
NoobRobe
Hats
table: 1A73C9D0
1
NoobHat
XP2
75
XP2
75
Levels
2
Levels
2
XP
0
XP
0
Here's a way to print all the elements without double or table reference values showing up.
As the name states, this function will print all the elements within a table, no matter how many nested tables there are inside it. I don't have a way to order them at the moment, but I'll update my answer if I find a way. You can also get rid of the empty spaces in the print line, I just used it so it would look neater. Let me know if it works.
function allElementsInTable(table)
for k,v in pairs(table) do
if type(table[k]) == 'table' then
print(k .. ":")
allElementsInTable(v)
else
print(" " .. k .. " = " .. tostring(v))
end
end
end
--place the name of your table in the parameter for this function
allElementsInTable(originalStats)
After more experimenting, I got this, if anyone wants it, feel free to use it.
tempData = { Info = {Visit = false, Name = 'None'},
Stats = {LVL = 0, XP = 0, Silver = 75},
Inventory = { Armors = {'BasicArmor'},
Weapons = {'BasicSword'} }
}
function Read()
for i, v in pairs(tempData['Info']) do
print(i..'\t',v)
end
----------
for i2, v2 in pairs(tempData['Stats']) do
print(i2..'\t',v2)
end
----------
for i3, v3 in pairs(tempData['Inventory']) do
print(i3..':')
for i4, v4 in pairs(v3) do
print('\t',v4)
end
end
end
Read()
Don't expect table's fields to be iterated with pairs() in some specific order. Internally Lua tables are hashtables, and the order of fields in it is not specified at all. It will change between runs, you can't have them iterated in the same order as they were filled.Only arrays with consecutive integer indices will maintain the order of their elements.

Constructing Key/Value Table in Lua

I'm trying to build a script for a MUD I play that will create a table to keep track of average xp for each mob. I'm having trouble with the syntax of checking whether an element in a table exists and if not creating it. I tried something like this but keep getting: attempt to index field '?' (a nil value)
mobz_buried = {
{mob = "troll", quantity = 2}
{mob = "warrior", quantity = 1}
{mob = "wizard", quantity = 1}} -- sample data
number_of_mobz_buried = 4
xp_from_bury = 2000 -- another script generates these values, these are all just examples
xp_per_corpse = xp_from_bury / number_of_mobz_buried
for _, v in ipairs(mobz_buried) do
if type(mobz[v].kc) == "variable" then -- kc for 'kill count', number of times killed
mobz[v].kc = mobz[v].kc + 1 -- if it exists increment kc
else
mobz[v].kc = 1 -- if it doesn't exist create a key value that matches the mobs name and make the kc 1
end
if type(mobz[v].xp) == "variable" then -- xp for average experience points
mobz[v].xp = (((mobz[v].kc - 1) * mobz[v].xp + xp_per_corpse)/mobz[v].kc) -- just my formula to find the average xp over a range of differant buries
else
mobz[v].xp = xp_per_corpse -- if it doesn't exist create the table just like before
end
end
I'm trying to end up with mobz.troll = {kc, xp}, mobz.warrior = {kc, xp}, mobz.wizard = {kc, xp} and the ability to add more key values based off of the names mobz_buried gives me.
Based on extra info from your comments, it sounds like you didn't construct a table for mobz. Try this:
local mobz = {}
for _, v in ipairs(mobz_buried) do
mobz[v.mob] = mobz[v.mob] or {}
mobz[v.mob].kc = (mobz[v.mob].kc or 0) + 1
-- etc...
end

Resources