Lua string.gsub inside string.gmatch? - lua

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.

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 remove repeated string values from a list

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

finding list of constant in an array

I have a list of constant:
FRUITS = [MANGO, BANANA, ORANGE, GUAVA]
and array which is superset of this constant, like
EDIBLE_ITEMS = [APPLE, CORN, MANGO, RICE, ORANGE, PAPAYA, LITCHI, RICE]
So now I need to test if any of the elements from FRUITS matches EDIBLE_ITEMS then call a function.
Any help will be appreciated. Thanks
Using Array#&:
FRUITS = ['MANGO', 'BANANA', 'ORANGE', 'GUAVA']
EDIBLE_ITEMS = ['APPLE', 'CORN', 'MANGO', 'RICE', 'ORANGE', 'PAPAYA', 'LITCHI', 'RICE']
(FRUITS & EDIBLE_ITEMS).any?
# => true
Do as below using Enumerable#any? :
EDIBLE_ITEMS = %w[APPLE, CORN, MANGO, RICE, ORANGE, PAPAYA, LITCHI, RICE]
FRUITS = %w[MANGO, BANANA, ORANGE, GUAVA]
FRUITS.any? { |item| EDIBLE_ITEMS.include? item } # => true

How can I get the number of instances of a value in a Rails array?

Say I have an array like this:
["white", "red", "blue", "red", "white", "green", "red", "blue", "white", "orange"]
I want to go through the array and create a new array containing each individual color and the amount of times it appeared in the original array.
So, in the new array it would report that "white" appeared 3 times, "blue" appeared 2 times and so on...
How should I go about doing this?
better return a hash...
def arr_times(arr)
arr.inject(Hash.new(0)) { |h,n| h[n] += 1; h }
end
counts = Hash.new(0)
colors.each do |color|
counts[color] += 1
end
result = {}
hash = array.group_by{|item| item}.each{|key, values| result[key] = values.size}
p result
I see you tagged the question with ruby-on-rails.
If this is a database model/column (e.g., User model with color attribute) you should be doing the computation in the DB:
User.count(:all,:group=>:color).sort_by {|arr| -arr[1]}

Resources