How to remove repeated string values from a list - lua

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

Related

LUA - Most common item in the table

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!

Find various ways of allocating fruit to people

# Example 1
People = ["Terry", "Merry"]
Fruit = ["Apple","Grape","Peach"]
# Possible solutions:
[
{"Terry"=>"Apple","Merry"=>"Grape"},
{"Terry"=>"Apple","Merry"=>"Peach"},
{"Terry"=>"Grape","Merry"=>"Apple"},
{"Terry"=>"Grape","Merry"=>"Peach"},
{"Terry"=>"Peach","Merry"=>"Apple"},
{"Terry"=>"Peach","Merry"=>"Grape"},
]
# Example 2
People = ["Terry", "Merry", "Perry"]
Fruit = ["Apple","Grape"]
# Possible solutions:
[
{"Terry"=>"Apple","Merry"=>"Grape","Perry"=>nil},
{"Terry"=>"Apple","Merry"=>nil,"Perry"=>"Grape"},
{"Terry"=>"Grape","Merry"=>"Apple","Perry"=>nil},
{"Terry"=>"Grape","Merry"=>nil,"Perry"=>"Apple"},
{"Terry"=>nil,"Merry"=>"Apple","Perry"=>"Grape"},
{"Terry"=>nil,"Merry"=>"Grape","Perry"=>"Apple"},
]
Stuck trying to solve this recursively (necessary for this exercise, though let me know if you don't think recursion is possible).
I feel like basically I start by assigning a random person a fruit, and then add that to all possible solutions that arise from the smaller subset of assigning remaining people remaining fruit.
E.g., for Example 1, I assign Terry an Apple, and then aggregate that with the remaining possible options of what Merry can get (either Grape or Peach).
Then just repeat changing up the fruit assigned to the first random person (e.g., with Terry getting Grape then Peach, in Example 1).
I feel like this sounds so straightforward but I'm struggling.
It can be done recursively as follows.
def hmmm(people, fruit)
adj_fruit = fruit + [nil]*([people.size-fruit.size, 0].max)
recurse(adj_fruit).map { |a| people.zip(a).to_h }
end
def recurse(fruit_left, fruit_selected = [])
return [fruit_selected + fruit_left] if fruit_left.size == 1
fruit_left.each_with_object([]) do |f,a|
recurse(fruit_left - [f], fruit_selected + [f]).each { |e| a << e }
end
end
hmmm(["Terry", "Merry"], ["Apple", "Grape", "Peach"])
#=> [{"Terry"=>"Apple", "Merry"=>"Grape"}, {"Terry"=>"Apple", "Merry"=>"Peach"},
# {"Terry"=>"Grape", "Merry"=>"Apple"}, {"Terry"=>"Grape", "Merry"=>"Peach"},
# {"Terry"=>"Peach", "Merry"=>"Apple"}, {"Terry"=>"Peach", "Merry"=>"Grape"}]
Here adj_fruit #=> ["Apple", "Grape", "Peach"]
hmmm(["Terry", "Merry", "Perry"], ["Apple", "Grape"])
#=> [{"Terry"=>"Apple", "Merry"=>"Grape", "Perry"=>nil},
# {"Terry"=>"Apple", "Merry"=>nil, "Perry"=>"Grape"},
# {"Terry"=>"Grape", "Merry"=>"Apple", "Perry"=>nil},
# {"Terry"=>"Grape", "Merry"=>nil, "Perry"=>"Apple"},
# {"Terry"=>nil, "Merry"=>"Apple", "Perry"=>"Grape"},
# {"Terry"=>nil, "Merry"=>"Grape", "Perry"=>"Apple"}]
Here adj_fruit #=> ["Apple", "Grape", nil].
We can see map's receiver in hmmm by removing .map { |a| people.zip(a).to_h } from its last line.
def hmmm(people, fruit)
adj_fruit = fruit + [nil]*([people.size-fruit.size, 0].max)
recurse(adj_fruit)
end
hmmm(["Terry", "Merry"], ["Apple","Grape","Peach"])
#=> [["Apple", "Grape", "Peach"], ["Apple", "Peach", "Grape"],
# ["Grape", "Apple", "Peach"], ["Grape", "Peach", "Apple"],
# ["Peach", "Apple", "Grape"], ["Peach", "Grape", "Apple"]]
A more conventional solution, such as the one following, would not employ recursion.
def hmmm(people, fruit)
(fruit + [nil]*[people.size - fruit.size, 0].max).
permutation(people.size).
map { |a| people.zip(a).to_h }
end
This produces the same return values as those shown above for the recursive solution.
See Array#permutation and Enumerable#zip.
If len(people) <= len(fruit), then you can use
for pieces in itertools.permutations(fruit, len(people)):
assign the pieces of fruit to the people in order
If len(people) > len(fruit), then use
for eaters in itertools.permutations(people, len(fruit))
assign the eaters to the fruit in order, and the others get nothing
I don't know how to combine the two separate cases into a single case
I now see that this was supposed to be solve recursively. Misread the original.
Let's look the possibilities for
assignment(people, fruit):
If len(people) == 0, then you're done, with the empty solution. (Not to be confused with no solution.)
If len(fruit) == 0, then no one gets any fruit. Again, this is an actual solution.
If len(people) <= len(fruit), then the first person gets some piece of fruit, appended onto all possible results of the remainder of the people getting the remainder of the fruit.
If len(people) > len(fruit), then either the first person does or doesn't get a piece of fruit, and recursively the rest of the people get whatever's left.
It's left as an exercise to you how to code this.
For anyone's future reference, this was my answer using recursion.
NOTE that "nil" overcounts; since "nil" is treated as a unique entry, the code reads {"Terry"=>"apple","Merry"=>"nil","Perry"=>"nil"} and {"Terry"=>"apple","Perry"=>"nil","Merry"=>"nil"} as 2 distinct solutions. I did not investigate further because this isn't super realistic for the exercise that this is a part of.
I also didn't investigate further for same reason, but using string "nil" versus nil yielded different results
def pure5(people, fruit, solution = [])
people_count = people.size
fruit_count = fruit.size
diff = people_count - fruit_count
diff.times { fruit << "nil" } if diff > 0
people.each do |p|
fruit.each do |f|
if people.size == 1
obj = {}
obj[p] = f
solution << obj
else
partial_solution = pure5(people - [p], fruit - [f])
partial_solution.each do |s|
s[p] = f
end
solution = solution + partial_solution
end
end
return solution
end
end

How to check bool nested in table in lua

I'm new to lua, and I'm having trouble with a basic sort-by-bool-condition thing for entries in a table.
`local tblFormReturn = {
{
['Name'] = 'Spike',
['Year'] = '10',
['House'] = 'Holmes',
['Form Returned'] = true
},
{
['Name'] = 'Elvis',
['Year'] = '11',
['House'] = 'Shaw',
['Form Returned'] = true
},
{
['Name'] = 'Michael',
['Year'] = '10',
['House'] = 'Langley',
['Form Returned'] = false
},
{
['Name'] = 'Chang',
['Year'] = '11',
['House'] = 'Holmes',
['Form Returned'] = false
}
}`
Basically, I want to be able to take this table, and for each chunk, check whether the kid is in Holmes house (1) and if they have returned their form (2). My feeling is I need to run a for-loop in pairs based off the lua manual, but I'm confused as to how I can access these values, given each chunk is sort of a sub-table. My attempts have all been based around something like this.
for i,'Form Returned' in tblFormReturned('Form Returned') do
if 'Form Returned' == true then
if 'House' == 'Holmes' then
print ('Number of Holmes forms returned' +1)
end
end
end
I'm not sure how to make this work. Any help greatly appreciated.
A few things of note here.
When you quote something (indicated by using the single quotes), you effectively make it a string.
A for loop loops through a table using ipairs (indexed pairs, such as yours is) or pairs (used on dictionary tables). Dictionary tables are considered those that are have a defined key rather than an index key (e.g. tblPets = {dog = "Fido", cat = "Sassy", duck = "Quackers} - this would allow you to return tblPets.dog (or tblPets["dog"]) to get the value).
Your print statement to add a number does not work. You cannot add a number to a string. Instead, you will need to set a count as a variable and add to it, provided it is a number.
Lastly, you can also combine the if statements into one to make it easier.
formCount = 0 -- This initializes the variable formCount as an interger, starting with 0.
for i,v in ipairs(tblFormReturned) do -- This iterates through the table
if v["Form Returned"] and v.House == "Holmes" then -- Looks to see if the form returned is true and house is Holmes. Note that with boolean values, you do not have to see if it equals true or false. if v["Form Returned"] == true and this format returns the same answer.
formCount = formCount + 1 -- Adds 1 to the formCount
end -- end if statement
end -- end for loop
Hopefully this helps a little with understanding. If you have any questions, don't hesitate to ask for clarification.

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.

Lua string.gsub inside string.gmatch?

I've created this simple example script to output a list of foods. If the food is a fruit then the color of the fruit will be displayed as well. The issue I'm having is in dealing with the irregular pluralization of 'strawberries.'
fruits = {apple = "green", orange = "orange", stawberry = "red"}
foods = {"potatoes", "apples", "strawberries", "carrots", "crab-apples"}
for _, food in ipairs(foods) do
for fruit, fruit_colour in pairs(fruits) do
duplicate = false
if (string.match(food, "^"..fruit) or string.match((string.gsub(food, "ies", "y")), "^"..fruit)) and not(duplicate) then -- this is where the troubles is!
print(food.." = "..fruit_colour)
duplicate = true
break
end
end
if not(duplicate) then
print(food)
end
end
Right now the program outputs:
potatoes
apples = green
strawberries
carrots
crab-apples
What I want is:
potatoes
apples = green
strawberries = red
carrots
crab-apples
I can't figure out why this doesn't work like I want it to!
Well for one thing, you miss-spelled strawberry here:
fruits = {apple = "green", orange = "orange", stawberry = "red"}
You can also work with lua tables as sets, which means that nested loop searching for duplicates is unnecessary. It can be simplified to something like:
fruits = {apple = "green", orange = "orange", strawberry = "red"}
foods = {"potatoes", "apples", "strawberries", "carrots", "crab-apples"}
function singular(word)
return word:gsub("(%a+)ies$", "%1y"):gsub("(%a+)s$", "%1")
end
for _, food in ipairs(foods) do
local single_fruit = singular(food)
if fruits[single_fruit] then
print(food .. " = " .. fruits[single_fruit])
else
print(food)
end
end
stawberry should be strawberry. The loop changes strawberries to strawberry and then tries to match strawberry against ^stawberry, but the typo causes it not to match.

Resources