I have a table like below, I don't need to know which product sells how much, I just want to know which product is the most popular. Do you have any idea which method is the best way to do this?
Like in the example below, knowing that because 3 of the product "blue" have been sold, it is the most popular
local products = {}
products["430373bb5b7a40a04f9"] = "red"
products["0dce4263af4b5cfe0de"] = "red"
products["cf2559afb736c1eb1bc"] = "green"
products["abc4d248541c3386c88"] = "blue"
products["bb9386c65270948ebee"] = "blue"
products["b193fba741cd646a9c0"] = "blue"
this example will count the number of sales in a single pass.
local products = {}
products["430373bb5b7a40a04f9"] = "red"
products["0dce4263af4b5cfe0de"] = "red"
products["cf2559afb736c1eb1bc"] = "green"
products["abc4d248541c3386c88"] = "blue"
products["bb9386c65270948ebee"] = "blue"
products["b193fba741cd646a9c0"] = "blue"
local pop = {}
for k,v in pairs(products) do
pop[v] = (pop[v] or 0) + 1
end
-- if you need to sort by sales then:
local pop_s = {}
for k,v in pairs(pop) do
pop_s[#pop_s+1] = { item=k, sales=v}
end
table.sort(pop_s, function (a,b) return a.sales>b.sales end)
for k,v in pairs(pop_s) do
print(v.item,v.sales)
end
result:
blue 3
red 2
green 1
To expand on Mike's answer: You could even sort the counts in linear (vs. O(n log n)) time since you know that the counts range from 1 to n; thus you can use a "bucket" approach where you use an index of products by count sold:
local products = {
["430373bb5b7a40a04f9"] = "red",
["0dce4263af4b5cfe0de"] = "red",
["cf2559afb736c1eb1bc"] = "green",
["abc4d248541c3386c88"] = "blue",
["bb9386c65270948ebee"] = "blue",
["b193fba741cd646a9c0"] = "blue",
}
local counts = {}
for _, product in pairs(products) do
counts[product] = (counts[product] or 0) + 1
end
local prods_by_cnt = {}
local max_cnt = 0
for product, count in pairs(counts) do
prods_by_cnt[count] = prods_by_cnt[count] or {}
table.insert(prods_by_cnt[count], product)
max_cnt = math.max(max_cnt, count)
end
local prods_sorted = {}
for cnt = 1, max_cnt do
for _, prod in ipairs(prods_by_cnt[cnt]) do
table.insert(prods_sorted, prod)
end
end
Side note: There is no need to build tables {item = ..., sales = ...} as long as you have the counts table: You can just keep a table of item names and use the item names to index the counts table. This may be slightly slower in practice because the counts table is larger and thus presumably more expensive to index - on the other hand, it is more memory-efficient; in theory, both are equally fast and memory efficient, requiring a constant time for lookup of the sales / counts and constant space to store each count.
Try this:
local products = {}
products["430373bb5b7a40a04f9"] = "red"
products["0dce4263af4b5cfe0de"] = "red"
products["cf2559afb736c1eb1bc"] = "green"
products["abc4d248541c3386c88"] = "blue"
products["bb9386c65270948ebee"] = "blue"
products["b193fba741cd646a9c0"] = "blue"
function GetCommonItem(arr)
local Count = 0
local Product = null
for i, v in pairs(arr) do -- Loops through the table
local Temp = 0
for e, f in pairs(arr) do -- Loops again to count the number of common products
if f = v then
Temp += 1
if Temp >= Count then -- Picks the product if it's the most common one
Count = Temp
Product = f
end
end
end
end
return Product
end
print("Most Common Product: ".GetCommonItem(products))
Didn't test so not sure if it works! Let me know the results after u try it!
Related
This is a continuation of a previous question.
I have a fixed list in Lua which I am reassigning values for
local a = {"apple", "pear", "orange", "kiwi", "tomato"}
local map = {
apple = "RD",
pear = "GR",
orange = "OG",
kiwi = "GR",
tomato = "RD",
banana = "YL",
}
colours = {}
for index = 1, #a do
table.insert(colours,map[a[index]or "OT")
end
Now I would either like to edit the existing script, or add some new script, to remove any repeated values.
My end result should be a table (colours) with no repeated values or empty strings, but I can't seem to think of a neat way to do this!
If it's not possible (or really messy) my second option would be to count the number unique values in the table.
If you don't want to run over the entire table every time you add an element you can simply create a second table where you remember which colours have been listed yet.
Simply use the colour as key.
local a = {"apple", "pear", "orange", "kiwi", "tomato"}
local map = {
apple = "RD",
pear = "GR",
orange = "OG",
kiwi = "GR",
tomato = "RD",
banana = "YL",
}
local listedColours = {}
local colours = {}
for _,colour in pairs(a) do
colour = map[colour] or "OT"
if not listedColours[colour] then
table.insert(colours, colour)
listedColors[colour] = true
end
end
Solution i suggest: add to table function contains
table.contains = function(t, value)
for index = 1, #t do
if t[index] == value then
return index
end
end
end
so problem with having only unique colours can be solved like:
for index = 1, #a do
local colour = map[a[index]] or "OT"
if not table.contains(colours, colour) then
table.insert(colours, colour)
end
end
I consider it pretty neat
--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.
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.
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
I need a piece of code in lua language that can find sequential items in an array that the number of item in the group exceeds a specific nubmer. Example:if I have the array(the numbers won't be in the right order, randomly distributed)->( 2,5,9,10,11,21,23,15,14,12,22,13,24 ) ; there are two sequential groups (9,10,11,12,13,14,15) and (21,22,23,24 ) . I want the first group to be found if the specific number say (4) or more, or I can get the two groups if the number is (3) or less for example.
thanks
The logical way would seem to be to reorder the table and look for gaps in the sequences.
function table.copy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
function groups(org, cnt)
-- Returns a table containing tables containing the groups found
local res = {}
local group = {}
tbl = table.copy(org) -- Prevent reordering of Original Table
table.sort(tbl)
local last = nil
for _,val in ipairs(tbl) do
if last and last + 1 ~= val then
if #group >= cnt then
table.insert(res,group)
end
group = {}
end
table.insert(group,val)
last = val
end
if #group >= cnt then
table.insert(res,group)
end
return res
end
local org = { 2,5,9,10,11,21,23,15,14,12,22,13,24 }
local result = groups(org,3)
print('Number of Groups',#result)