Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

How do I check if there's any 2 items in a table?

Asked by
yumtaste 476 Moderation Voter
9 years ago

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!

1 answer

Log in to vote
2
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
9 years ago

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.

0
Good answer Mystdar 352 — 9y
0
Excellent answer. This is EXACTLY what I was looking for. Thank you, my good man! yumtaste 476 — 9y
0
Wow. It's even better htan before. BlueTaslem, you are a genius! yumtaste 476 — 9y
Ad

Answer this question