If table contains a key with specific value - lua

It might be a bit confusing, but I have a table, for example, called Ant.
This table, contains a bunch of other (unnamed) tables. These tables represent ants, and hold values.
Kind of like this:
Ant = {
{age=3,speed=10},
{age=6,speed=7}
}
My question is, how would I check if any of the unnamed table inside of the Ant table contains a specific value to age.
So, for example, I'd like to check if any of my ants are aged 3 years old.
I hope I was clear enough, and thanks in advance!

You can loop through the table and check:
for i, v in ipairs(Ant) do
if v.age == 3 then
print( i )
end
end
It'll print the index at which your 3 year old ants are stored.

If all you need to check the value of age in each sub-table, building a custom iterator is another way:
function age_iter(t)
local i = 0
return function()
i = i + 1
return t[i] and t[i].age
end
end
To iterate over all the age value would be:
for age in age_iter(Ant) do
print(age)
end
It's easy to modify it to check if one of the age value is equal to 3.

Related

Lua 5.0 - iterations over tables ignore duplicate keys even though values are different

Reading the injected comments in the Code Snippet should give enough context.
--| Table |--
QuestData = {
["QuestName"]={
["Quest Descrip"]={8,1686192712},
["Quest Descrip"]={32,1686193248},
["Quest Descrip"]={0,2965579272},
},
}
--| Code Snippet |--
--| gets QuestName then does below |--
if QuestName then
-- (K = QuestName) and (V = the 3 entries below it in the table)
for k,v in pairs(QuestData) do
-- Checks to make sure the external function that obtained the QuestName matches what is in the table before cont
if strlower(k) == strlower(QuestName) then
local index = 0
-- Iterates over the first two pairs - Quest Descrip key and values
for kk,vv in pairs(v) do
index = index + 1
end
-- Iterates over the second two pairs of values
if index == 1 then
for kk,vv in pairs(v) do
-- Sends the 10 digit hash number to the function
Quest:Function(vv[2])
end
end
end
end
end
The issue I'm running into is that Lua will only pick up one of the numbers and ignore the rest. I need all the possible hash numbers regardless of duplicates. The QuestData table ("database") has well over 10,000 entries. I'm not going to go through all of them and remove the duplicates. Besides, the duplicates are there because the same quest can be picked up in more than one location in the game. It's not a duplicate quest but it has a different hash number.
Key is always unique. It is the point of the key, that the key is pointing to unique value and you can't have more keys with same name to point different values. It is by definition by Lua tables.
It is like if you would want to have two variables with same name and different content. It does not make sense ...
The table type implements associative arrays. [...]
Like global variables, table fields evaluate to nil if they are not initialized. Also like global variables, you can assign nil to a table field to delete it. That is not a coincidence: Lua stores global variables in ordinary tables.
Quote from Lua Tables
Hashing in Lua
Based on comments, I update the answer to give some idea about hashing.
You are using hashing usually in low-level languages like C. In Lua, the associative arrays are already hashed somehow in the background, so it will be overkill (especially using SHA or so).
Instead of linked lists commonly used in C, you should just construct more levels of tables to handle collisions (there is nothing "better" in Lua).
And if you want to have it fancy set up some metatables to make it somehow transparent. But from your question, it is really not clear how your data look like and what you really want.
Basically you don't need more than this:
QuestData = {
["QuestName"]={
["Quest Descrip"]={
{8,1686192712},
{32,1686193248},
{0,2965579272},
},
},
}
As Jakuje already mentioned table keys are unique.
But you can store both as a table member like:
QuestData = {
-- "QuestName" must be unique! Of course you can put it into a table member as well
["QuestName"]={
{hash = "Quest Descrip", values = {8,1686192712} },
{hash = "Quest Descrip", values = {32,1686193248} },
{hash = "Quest Descrip", values = {0,2965579272} }
}
}
I'm sure you can organize this in a better way. It looks like a rather confusing concept to me.
You've said you can't "rewrite the database", but the problem is the QuestData table doesn't hold what you think it holds.
Here's your table:
QuestData = {
["QuestName"]={
["Quest Descrip"]={8,1686192712},
["Quest Descrip"]={32,1686193248},
["Quest Descrip"]={0,2965579272},
},
}
But, this is actually like writing...
QuestData["Quest Descrip"] = {8,1686192712}
QuestData["Quest Descrip"] = {32,1686193248}
QuestData["Quest Descrip"] = {0,2965579272}
So the second (and then, third) values overwrite the first. The problem is not that you can't access the table, but that the table doesn't contain the values any more.
You need to find a different way of representing your data.

Potential problems with storing the second table of two dimensional arrays in the index

In the past I found myself using a table as index and value of
a table when the order was irrelevant.
Since every table returns a unique value they are save to use as
index and with that I already got all the information I want to
use later on in the program. Now I did not see any similar lua code
jet and didn't use it in a non test-program. So I'm worrying that I
might get some unforeseen/unexpected problems when using this method.
example:
a = {1,2,3,4,5} --some testing values
b = {2,nil,4,nil,1}
c = {3,nil,nil,nil,2}
d = {4,nil,1,nil,3}
e = {5,1,2,3,4}
tab = {a,b,c,d,e}
t = {}
for i, v in pairs(tab) do
t[v] = 0
end
for iv in pairs(t) do --is almost every time outputting it in a different order
print(iv[1],iv[2],iv[3],iv[4],iv[5]) --could be a list of data where you have to go through all of it anyway
end
io.read()
Now I can store some additional information in t[v] but if I don't have
any is there maybe some lua-type that is smaller?
Edit:
Does this go well with the use of weak-tables?
Note:
Standard 2d table: table[key1] = table
table[key1][key2] <-- contains stuff
this version: table[table] = anything but nil <-- not accessible over table[key1][key2]
key1[key2] <-- contains stuff
It's fine to use a table as a key in another table.
However, note that different tables will be different keys, even of the tables have the same contents.

table.insert/remove by value

I got two tables, for example:
table1 = { element1, element2, element3, element4 }
table2 = { element1, element3 }
Table 2 refers to some elements of table1, but I don't know which exactly, nor I know their index.
Now, for an specific element I want to check if table2 does contains it or not and insert/remove it in the case.
First thing that jumped to my mind was:
table.remove/insert(table2, table1.elementX)
But due insert/remove does its lookup by index, this doesn't work.
Sure, I could iterate through the whole table until I find the element and remove it, respectively until Iteration is done without match and insert it.
But is there a more performant method to do this?
I do not want to fill table2 with empty fields for bringing the elements on matching indices.
To insert, it's quite straightforward:
table.insert(table1, table2[index])
Unfortunately, to remove, it's a bit more tricky:
local ids = {} -- table containing ids to remove
for i,v in ipair(table1) do
if v == table2[index] then
table.insert(ids, 1, i) -- "1" preprends the value
end
-- At this point, "ids" contains all the ids to remove in the reverse order
for k,v in pair(ids) do
table.remove(table1, v)
end
What happens here is:
An intermediate table is created, it only contains the ids of the table to remove, descending. For example: { 6, 3, 1} (if the value is present 3 times).
That intermediate table is used to update the main table, as you can't use the ids from a table you are updating in a loop (that's what the comments about "transversal" mean).
Note that those operations must be made from the end of the table, because removing an element will change the ids of the following ones.
First revert table2 with
table2reverse = {}
for k,v in pairs(table2) do table2reverse[v]=k end
Then do this:
for k,v in pairs(table1)do
if table2reverse[v] then
table1[k]=nil
end
Finally compact table1.
for k,v in pairs(table1)do
if v == table2[index] then
table.remove/insert(table1, k)
break
end
Of course this works, but I still hope there's a more performante solution.
Due in case of multiple 1000 entrys in table1 and multiple 100 entrys in table2, this is will lead to high cpu usage, wich I want to avoid. (programming a controller with only 200mhz)

Corona SDK (LUA) - Trouble With Inserting Into Table

I have searched around quite a bit and couldn't quite find a solution for this. Any help you can offer would be appreciated.
-- The array compiled of enemies only allowed in the current
-- level phase
local EnemyList = {}
-- Counter to determine the next spot in the EnemyList
-- array to insert into
local counter = 1
for i=1,#Enemies do
if Enemies[i].phase == 0 or Enemies[i].phase == which_phase then
EnemyList[counter].src = Enemies[i].src
EnemyList[counter].exp = Enemies[i].exp
counter = counter + 1
end
end
I am getting an error about attempting to index a nil value, in reference to the EnemyList table/array. What I am trying to accomplish is I am trying to compile a new array of only enemies that are allowed. I guess I am unsure how to insert a new row into the EnemyList table. I tried using table.insert, but the value parameter is required, and I am not sure how to do that with the fact that I am storing multiple values into the EnemyList array.
Any help or insight on the proper way to insert a new row into an empty table/array would be much appreciated. Thanks!
EDIT:
I got a working solution, but I figured I should update the code here if anyone in the future finds it.
-- The array compiled of enemies only allowed in the current
-- level phase
local EnemyList = {}
for i=1,#Enemies do
if Enemies[i].phase == 0 or Enemies[i].phase == which_phase then
table.insert( EnemyList, { src = Enemies[i].src, exp = Enemies[i].exp } )
end
end
You can store tables within tables in Lua. Tables are indexed in one of two ways: First, by index number. This is what table.insert uses; it will add an entry at the next index number.
The second way is by key; e.g.
> t = {}
> t.test = {}
> =t.test
table: 0077D320
You can insert tables into tables; this is how you create a 2D table. Because of the way you've defined your tables, type(EnemyList[counter]) = table.
You can insert new entries into tables by running table.insert(table, value). This will assign value to the next available numeric entry. type(value) can also be a table; this is how you create "multidimensional arrays" in Lua.
As an aside, instead of using for i=1,#Enemies I would suggest using for i,v in ipairs(Enemies). The second one will iterate over all numerical entries in the Enemies table.

How can I compress a table after having removed a value from it?

I have a table which contains 4 values.
For example:
2
4
1
3
I use a function to step through the table looking for, lets say the number 1 by using pairs and to get the position of it in the table.
I then use table.remove to remove 1 from that position. What I would like to do now is to compress the table so that it is 3 values long
2
4
3
I'm fairly new to LUA so be gentle with me. :)
What I have is pretty much this:
CloseRandomConsole = math.random(1,(#ConsoleTable))
If CloseRandomConsole == 1 then
for key, value in pairs(ConsoleTable) do
if value == "1" then
table.remove(ConsoleTable, key)
break
end
end
I see where I'm going wrong but I hae no idea how to solve it.
math.random(1,(#ConsoleTable))
I only want to be able to random between one of the values in the table. And when I have randomed that vlue I want it removed so that I will be left with three other values to random from.
Am I confusing you? :)
What do you mean?
s = {2,4,1,3} -- the table
for k,v in pairs(s) do
if v==1 then
table.remove(s,k)
end
end
print(#s) -- is now 3
for k,v in pairs(s) do print(v) end -- just the 3 values ...
#Vitae: When you're asking about anything, you should describe what you want to do, not how you want to do it -- especially when you have no idea what you're doing ...
Maybe you want to remove a random value from the table? Then fetch the value at a random index ...
function poprandom( t )
local idx = math.random(1,#t)
local ret = t[idx]
table.remove(t, idx)
return ret
end

Resources