Lua - Table.hasValue returning nil - lua

I have a table something like this:
table = {milk, butter, cheese} -- without "Quotation marks"
I was searching for a way to check if a given value is in the table or not, and found this:
if table.hasValue(table, milk) == true then ...
but it returns nil, any reason why? (it says .hasValue is invalid) or can I get an alternative to check if value exists in that table? I tried several ways like:
if table.milk == true then ...
if table[milk] == true then ...
All of these returns nil or false.

you can try this
items = {milk=true, butter=true, cheese=true}
if items.milk then
...
end
OR
if items.butter == true then
...
end

A Lua table can act as an array or as an associative array (map).
There is no hasValue, but by using a table as an associative array you can easily implement it efficiently:
local table = {
milk = true,
butter = true,
cheese = true,
}
-- has milk?
if table.milk then
print("Has milk!")
end
if table.rocks then
print("Has rocks!")
end

You have a few options here.
One, is to create a set:
local set = {
foo = true,
bar = true,
baz = true
}
Then you check if either of these are in the table:
if set.bar then
The drawback to this approach is that you won't iterate over it in any specific order (pairs returns items in an arbitrary order).
Another option would be to use a function to check each value in a table. This'll be very slow in large tables, which brings us to back to a modification of the first option: A reverse lookup generator: (This is what I'd recommend doing -- unless your set is static)
local data = {"milk", "butter", "cheese"}
local function reverse(tbl, target)
local target = target or {}
for k, v in pairs(tbl) do
target[v] = k
end
return target
end
local revdata = reverse(data)
print(revdata.cheese, revdata.butter, revdata.milk)
-- Output: 3 2 1
This'll generate a set (with the added bonus of giving you the index where the value was in your original table). You can also put the reverse into the same table as the data was in, but this won't go well with numbers (and it'll be messy if you need to generate the reverse again).

If you write table = {milk=true, butter=true, cheese=true}, then you can use if table.milk == true then ....

Related

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.

Lua Table Access with Location Given as a String

I have a table and I'm trying to access a specific location that is passed in as a String. What is the easiest way to use the string to access the correct location?
Example, if the table looks like this:
a.b1 = true
a.b2.c1 = true
a.b2.c2 = false
a.b3 = true
How can I change a.b2.c2 to true given a location 'a.b2.c2' as a string.
If you have just a single level, you can use square-brace indexing:
function setSingle(obj, key, value)
obj[key] = value
end
setSingle(a, "b1", "foo")
print(a.b1) --> foo
If you have multiple, you need to do several iterations of this indexing. You can use a loop to do that:
function setMultiple(obj, keys, value)
for i = 1, #keys - 1 do
obj = obj[keys[i]]
end
-- Merely "obj = value" would affect only this local variable
-- (as above in the loop), rather than modify the table.
-- So the last index has to be done separately from the loop:
obj[keys[#keys]] = value
end
setMultiple(a, {"b2", "c1"}, "foo")
print(a.b2.c1) --> foo
You can use string.gmatch to parse a properly formatted list of keys. [^.]+ will match "words" made of non-period symbols:
function parseDots(str)
local keys = {}
for key in str:gmatch "[^.]+" do
table.insert(keys, key)
end
return keys
end
Putting this all together,
setMultiple(a, parseDots("b2.c2"), "foo")
print(a.b2.c2) --> foo
One issue you may run into is that you cannot create new tables with this function; you will have to create the containing table before you can create any keys in it. For example, beforing ading "b4.c3" you would have to add "b4".
You can use loadstring to build the statement you want to execute as a string.
a = { b2 = {} }
a.b1 = true
a.b2.c1 = true
a.b2.c2 = false
a.b3 = true
str = "a.b2.c2"
loadstring(str .. " = true")()
print(a.b2.c2)

Finding duplicates in a multi-dimensional table

A slightly altered version of the below permitted me to filter unique field values out of a multi-dimensional table (dictionary style).
[ url ] http://rosettacode.org/wiki/Remove_duplicate_elements#Lua
items = {1,2,3,4,1,2,3,4,"bird","cat","dog","dog","bird"}`
flags = {}
io.write('Unique items are:')
for i=1,#items do
if not flags[items[i]] then
io.write(' ' .. items[i])
flags[items[i]] = true
end
end
io.write('\n')`
What I'm lost at is what 'if not ... then ... end' part actually does. To me this is sillyspeak but hey, it works ;-) Now i want to know what happens under the hood.
I hope multi-dimensional does not offend anyone, I'm referring to a table consisting of multiple row containing multiple key-value pairs on each row.
Here's the code i'm using, no brilliant adaptation but good enough to filter unique values on a fieldname
for i=1,#table,1 do
if not table2[table[i].fieldname] then
table2[table[i].fieldname] = true
end
end
for k,v in pairs(table2) do
print(k)
end
function findDuplicates(t)
seen = {} --keep record of elements we've seen
duplicated = {} --keep a record of duplicated elements
for i = 1, #t do
element = t[i]
if seen[element] then --check if we've seen the element before
duplicated[element] = true --if we have then it must be a duplicate! add to a table to keep track of this
else
seen[element] = true -- set the element to seen
end
end
return duplicated
end
The logic behind the if seen[element] then, is that we check if we've already seen the element before in the table. As if they key doesn't exist nill will be returned which is evaluated which is false (this is not the same as boolean false, there are two types of false in lua!).
You can use this function like so:
t = {'a','b','a','c','c','c','d'}
for key,_ in pairs(findDuplicates(t)) do
print(key)
end
However that function won't work with multidimensional tables, this one will however:
function findDuplicates(t)
seen = {} --keep record of elements we've seen
duplicated = {} --keep a record of duplicated elements
local function traverse(subt)
for i=1, #subt do
element = subt[i]
if type(element) == 'table' then
traverse(element)
else
if seen[element] then
duplicated[element] = true
else
seen[element] = true
end
end
end
end
traverse(t)
return duplicated
end
Example useage:
t = {'a',{'b','a'},'c',{'c',{'c'}},'d'}
for k,_ in pairs(findDuplicates(t)) do
print(k)
end
Outputs
a
c
t = {a='a',b='b',c='c',d='c',e='a',f='d'}
function findDuplicates(t)
seen = {}
duplicated = {}
for key,val in pairs(t) do
if seen[val] then
duplicated[val] = true
else
seen[val] = true
end
end
return duplicated
end
This works the same way as before but checks if the same value is associated with a different key and if so, makes note of that value as being duplicated.
Eventually this is the code which worked for me. I've been asked to post it as a separate answer so here goes.
for i=1,#table1,1 do
if not table2[table1[i].fieldname] then
table2[table1[i].fieldname] = true
end
end
for k,v in pairs(table2) do
print(k)
end

Trying to compare all entries of one table in Lua

Basically I have a table of objects, each of these objects has one particular field that is a number. I'm trying to see if any of these numerical entries match up and I can't think of a way to do it. I thought possibly a double for loop, one loop iterating through the table, the other decrementing, but won't this at some point lead to two values being compared twice? I'm worried that it may appear to work on the surface but actually have subtle errors. This is how I pictured the code:
for i = #table, 1, -1 do
for j = 1, #table do
if( table[i].n == table[j].n ) then
table.insert(table2, table[i])
table.insert(table2, table[j])
end
end
end
I want to insert the selected objects, as tables, into another pre made one without any duplicates.
Let the outer loop run over the table, and let the inner loop always start one element ahead of the outer one - this avoids double counting and comparing objects with themselves. Also, if you call the table you want to examine table that will probably hide the table library in which you want to access insert. So let's say you call your input table t:
for i = 1, #t do
for j = i+1, #t do
if( t[i].n == t[j].n ) then
table.insert(table2, t[i])
table.insert(table2, t[j])
end
end
end
Still, if three or more elements have the same value n you will add some of them multiple times. You could use another table to remember which elements you've already inserted:
local done = {}
for i = 1, #t do
for j = i+1, #t do
if( t[i].n == t[j].n ) then
if not done[i] then
table.insert(table2, t[i])
done[i] = true
end
if not done[j] then
table.insert(table2, t[j])
done[j] = true
end
end
end
end
I admit this isn't really elegant, but it's getting late over here, and my brain refuses to think of a neater approach.
EDIT: In fact... using another table, you can reduce this to a single loop. When you encounter a new n you add a new value at n as the key into your helper table - the value will be that t[i] you were just analysing. If you encounter an n that already is in the table, you take that saved element and the current one and add them both to your target list - you also replace the element in the auxiliary table with true or something that's not a table:
local temp = {}
for i = 1, #t do
local n = t[i].n
if not temp[n] then
temp[n] = t[i]
else
if type(temp[n]) == "table" then
table.insert(table2, temp[n])
temp[n] = true
end
table.insert(table2, t[i])
end
end

Comparing two index tables by index-value in lua

I'm attempting to compare two tables of equal length with a function, since I don't know of any other way to do so. However, with the following function, it fails to register, and I've no clue why. I'm hoping someone can provide insight to this problem or has a better way of comparing the two tables.
The tables are being populated with the following code:
str = "parameters determined by program (all digits)"
tableone = {}
for word in str:gmatch("%d") do table.insert(tableone,word) end
It's identical for both tables, except, of course, the individual table names. The tables are being populated properly, and display properly when I print them. Here are two tables for the sake of this question:
tableone = {}
tabletwo = {}
for i=1,4 do table.insert(tableone, i) end
for i=1,4 do table.insert(tabletwo, i) end
Obviously, these two tables are going to be equal to each other. The function I wrote to compare the index tables is as follows:
function comparetables(t1, t2)
matchct = 0
for i=1,#t1 do
if t1[i] == t2[i] then
matchct = matchct + 1
end
if matchct == #t1 then
return true
end
end
I tried doing
print(comparetables(tableone,tabletwo))
to see if it'll print "true" but no luck. To me, it seems like it should work without a problem. Yet it doesn't. What am I missing? I've tried searching for something like a table.compare function that someone may have already written, but no such luck in finding one. Thanks for any suggestions!
Additional information:
The reason I'm comparing tables is for a mastermaind-type game. That means the following three rules must apply when comparing tables. The function I created was to just get me started, thinking I could work from there.
When comparing the tables, if the numbers match, Ccount increases by 1.
When comparing tables, if the value exists in a different index position, increment Pcount by 1
For example, with a table of values {1, 3, 3, 4} and a guess of {4, 4, 3, 1}, it would return Pcount of 2 (the one 4 and the 1) and a Ccount of 1 (the three in the third position). I think one of the hardest parts is going to be getting the comparison to recognize that the second 4 in the guess should not increment the Pcount at all.
A slight variant on your code that should work is:
function comparetables(t1, t2)
if #t1 ~= #t2 then return false end
for i=1,#t1 do
if t1[i] ~= t2[i] then return false end
end
return true
end
However I use something more like this: It checks the types of the arguments, their metatables, and a few other cases.
-- This is not clever enough to find matching table keys
-- i.e. this will return false
-- recursive_compare( { [{}]:1 }, { [{}]:1 } )
-- but this is unusual enough for me not to care ;)
-- It can also get stuck in infinite loops if you use it on
-- an evil table like this:
-- t = {}
-- t[1] = t
function recursive_compare(t1,t2)
-- Use usual comparison first.
if t1==t2 then return true end
-- We only support non-default behavior for tables
if (type(t1)~="table") then return false end
-- They better have the same metatables
local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if( not recursive_compare(mt1,mt2) ) then return false end
-- Check each key-value pair
-- We have to do this both ways in case we miss some.
-- TODO: Could probably be smarter and not check those we've
-- already checked though!
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if( not recursive_compare(v1,v2) ) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if( not recursive_compare(v1,v2) ) then return false end
end
return true
end
Here's an example of it in use:
print( recursive_compare( {1,2,3,{1,2,1}}, {1,2,3,{1,2,1}} ) ) -- prints true
print( recursive_compare( {1,2,3,{1,2,1}}, {2,2,3,{1,2,3}} ) ) -- prints false
If you're comparing objects that are more objecty than tabley in an Object oriented sense, then I'd look at implementing the functions in the lua OO way.
Something like this should do the trick:
GameState = {}
GameState.mt = {}
GameState.mt.fns = {}
GameState.mt.__index = GameState.mt.fns
function GameState.new(a,b,c,d)
-- TODO: put argument checks here...
local retval = {}
retval[1] = a
retval[2] = b
retval[3] = c
retval[4] = d
setmetatable(retval, GameState.mt)
return retval
end
function GameState.mt.fns.print( self )
print(" GameState: ", self[1], self[2], self[3], self[4] )
end
function GameState.mt.__tostring( self )
return "GameState: "..self[1].." "..self[2].." "..self[3].." "..self[4]
end
function GameState.mt.__eq(self, other)
-- Check it's actually a GameState, and all its bits match
return getmetatable(other)==GameState.mt and
(self[1] == other[1]) and
(self[2] == other[2]) and
(self[3] == other[3]) and
(self[4] == other[4])
end
Then you'd use it like this:
state1 = GameState.new(1,2,3,4)
state2 = GameState.new(1,2,3,4)
print("State 1 is:")
state1:print()
print("State 2 is:")
print(state2)
print( "state1 == state2 : ", state1 == state2 )
print( "Changing state 2")
state2[1]=2
print( "state1 == state2 : ", state1 == state2 )

Resources