I'm making a sort of crafting system for my game, and I imagine the most efficient way to carry it out would be if I had a table of selected ingredients, and a method to see if there's 2 items in any order in the selected table. Example: I want to check if there's the string "foo1" anywhere in the table, and I also want to see if there's "bar2" in the table anywhere. Feel free to ask questions to verify what I'm asking, and thanks for your help!
How do I check if there's any 2 items in a table?
1 answer
First, solve the problem of finding an item in a table (we will say where it is in particular):
function find(tab, element)
for index, value in pairs(tab) do
if value == element then
return index
end
end
end
find will return nil if element wasn't in tab, otherwise it will return where in tab it is, ie, tab[find(tab, element)] == element.
Thus to check if something is in a table:
if find(tab, "foo") then
-- "foo" is in `tab`
We could just string a few together to see if they have multiple things:
if find(tab, "foo") and find(tab, "bar") then
-- they have both "foo" and "bar"
It's also straightforward to use a loop to check if all of the things are in the table:
local required = {"foo", "bar"}
local haveAll = true -- (have all *so far*)
for _, thing in pairs(required) do
haveAll = haveAll and find(tab, thing)
end
if haveAll then
-- they have all the things
The limitation of this solution is that we can't require more than one a thing.
if find(tab, "foo") and find(tab, "foo") then
-- same thing as
if find(tab, "foo") then
-- since both calls will return the same thing
Thus we could make a "count" (or find multiple) function:
function findAll(tab, element)
local where = {}
for index, value in pairs(tab) do
if value == element then
table.insert(where, index)
end
end
end
Now findAll will return an empty list if it isn't in tab, otherwise it will return a list of elements.
Thus
if #findAll(tab, "foo") >= 1 then
-- they have at least one foo
And we can require 2 foo and 3 bar like this:
if #findAll(tab, "foo") >= 2 and findAll(tab, "bar") >= 3 then
To put this into a loop, we can make a bunch of pairings:
local required = { {"foo", 2}, {"bar", 3} }
local haveAll = true
for _, thing in pairs(required) do
-- thing[1]: ingrediant
-- thing[2]: quantity
haveAll = haveAll and ( #findAll(tab, thing[1]) >= thing[2] )
end
Bonus option: Say you don't want to make the tables of tables like above. E.g., you want ingredients to look like this:
local required = {"foo", "foo", "bar", "bar", "bar"}
-- Arguably this is a worse format because you could miscount the
-- number; still, it is probably more natural.
Then we can just go through all of the elements of required and count how many times they appear!
local haveAll = true -- (have all *so far*)
for _, thing in pairs(required)
haveAll = haveAll and ( #findAll(tab, thing) >= #findAll(required, thing) )
end
if haveAll then
This is somewhat inefficient because it will re-check for, say, "bar" three times -- but the behavior will be correct, and this will still be very fast for small lists like ingredient lists.
This discussion is preserved as it was. New answers, comments, and votes are closed.

3 comments
Good answer
Excellent answer. This is EXACTLY what I was looking for. Thank you, my good man!
Wow. It's even better htan before. BlueTaslem, you are a genius!