Lua search tables using index or value - lua

So if I have a table of colours:
colour["red"] = 1
colour["blue"] = 4
colour["purple"] = 5
and I want to add red to blue, I can easily get the number values of red and blue, but then with the value 5, can I get it to return "purple" without scanning the whole table?

You would need a table with both hash and array part, if colour numbers are unique. For example:
colour["purple"] = 5
colour[5] = "purple"
You can create a little helper function that would facilitate populating the table, such as:
function addColour(coltab, str, val)
coltab[str] = val
coltab[val] = str
end

#W.B.'s answer is good, if you want something more magic you can use this variation using the __newindex metamethod:
local colour = setmetatable({}, {
__newindex = function(self,k,v)
rawset(self,k,v)
rawset(self,v,k)
end
})
colour["red"] = 1
colour["blue"] = 4
colour["purple"] = 5
print(colour["purple"]) -- 5
print(colour[4]) -- blue

Related

Index values from table into another table

I want to store the values by selecting the keys of a table into another table, for example:
polyline = {color="blue", thickness=2, npoints=4}
stuff = {"polyline.color":[polyline.thickness]}
print(stuff)
Should produce:
blue 2
However, I get the following error:
input:3: '}' expected near ':'
local polyline = {color="blue", thickness=2, npoints=4}
local stuff = {polyline.color, polyline.thickness}
print(table.unpack(stuff))
I believe, You're mixing in some Python syntax. Do you notice using two different (wrong) ways of accessing the values?
I guess, this is what You've meant with your snippet of Lua code:
polyline = {color = "blue", thickness = 2, npoints = 4}
stuff = {[polyline.color] = polyline.thickness}
for key, val in pairs(stuff) do
print(key, val)
end

Lua and Love2D, table in table error

Why isn't this working? I'm trying to put all my object tables in a single table and use a forloop to iterate through each of them and draw. It shows an error message saying: "}" expected near "=" at line 5
function love.load()
solidstatic = {
ground = {x = 0,y = 160,width = 1000,height = 1000},
box = {x = 80,y = 100,width = 15,height = 15}
}
end
function love.draw()
for i,obj in ipairs(solidstatic) do
love.graphics.rectangle("fill",obj[x],obj[y],obj[width],obj[height])
end
end
(edit) solved the error problem, I was running the wrong .lua file. But still, it doesn't draw anything on the screen
Two things.
Firstly, you must use pairs instead of ipairs to list keys that are not numbers.
for i, v in pairs(table) do
...
end
You must also index the variables as a string.
t = {
x = 1
}
t['x'] = 1
-- or
t.x = 1
This is because doing it without quotes would be indexing with the global variable x, which doesn't exist.
You need to use pairs instead of ipairs to iterate over elements in solidstatic as there are no array keys in that table.

Randomly select a key from a table in Lua

I want to randomly populate a grid in Lua using a list of possible items, which is defined as follows:
-- Items
items = {}
items.glass = {}
items.glass.color = colors.blue
items.brick = {}
items.brick.color = colors.red
items.grass = {}
items.grass.color = colors.green
So the keys of the table are "glass", "brick" and "grass".
How do I randomly select one of these keys if they are not addressable by a numeric index?
Well, I kind of got a workaround, but I would be open to any better suggestions.
The first solution consists of having a secondary table which serves as an index to the first table:
item_index = {"grass", "brick", "glass"}
Then I can randomly store a key of this table (board is a matrix that stores the value of the random entry in item_index):
local index = math.random(1,3)
board[i][j] = item_index[index]
After which I can get details of the original list as follows:
items[board[y][x]].color
The second solution, which I have decided on, involves adding the defined elements as array elements to the original table:
-- Items
items = {}
items.glass = {}
items.glass.color = colors.blue
table.insert(items, items.glass) --- Add item as array item
items.brick = {}
items.brick.color = colors.red
table.insert(items, items.brick) --- Add item as array item
items.grass = {}
items.grass.color = colors.green
table.insert(items, items.grass) --- Add item as array item
Then, I can address the elements directly using an index:
local index = math.random(1,3)
board[i][j] = items[index]
And they can be retrieved directly without the need for an additional lookup:
board[y][x].color
Although your second method gives concise syntax, I think the first is easier to maintain. I can't test here, but I think you can get the best of both, won't this work:
local items = {
glass = {
color = colors.blue,
},
brick = {
color = colors.red,
},
grass = {
color = colors.green,
},
}
local item_index = {"grass", "brick", "glass"}
local index = math.random(1,3)
board[i][j] = items[item_index[index]]
print('color:', board[i][j].color)
If you're table is not too big and you can just break off at a random point. This method assumes that you know the number of entries in the table (which is not equal to #table value, if the table has non-number keys).
So find the length of the table, then break at random(1, length(table)), like so:
local items = {} ....
items.grass.color = colors.green
local numitems = 0 -- find the size of the table
for k,v in pairs(items) do
numitems = numitems + 1
end
local randval = math.random(1, numitems) -- get a random point
local randentry
local count = 0
for k,v in pairs(items) do
count = count + 1
if(count == randentry) then
randentry = {key = k, val = v}
break
end
end
The goods: You don't have to keep track of the keys. It can be any table, you don't need to maintain it.
The bad and ugly: It is O(n) - two linear passes.So, it is not at all ideal if you have big table.
The above answers assume you know what all of the keys are, which isn't something I was able to do earlier today. My solution:
function table.randFrom( t )
local choice = "F"
local n = 0
for i, o in pairs(t) do
n = n + 1
if math.random() < (1/n) then
choice = o
end
end
return choice
end
Explanation: we can't use table.getn( t ) to get the size of the table, so we track it as we go. The first item will have a 1/1=1 chance of being picked; the second 1/2 = 0.5, and so on...
If you expand for N items, the Nth item will have a 1/N chance of being chosen. The first item will have a 1 - (1/2) - (1/3) - (1/4) - ... - (1/N) chance of not being replaced (remember, it is always chosen at first). This series converges to 1 - (N-1)/N = 1/N, which is equal to the chance of the last item being chosen.
Thus, each item in the array has an equal likelihood of being chosen; it is uniformly random. This also runs in O(n) time, which isn't great but it's the best you can do if you don't know your index names.

Lua Nested Table Getting Elements

I have a nested table like so:
t1 ={}
t1[1] = {col1=1,col2=1,col3=1,col4=1}
t1[2] = {col1=1,col2=1,col3=1,col4=1}
t1[3] = {col1=1,col2=1,col3=1,col4=1}
t1[4] = {col1=1,col2=1,col3=1,col4=1}
it's actually much larger with 250 items in t1 and 30 items per nested table so what I want to do is loop through and get sub table values like this:
for i = 2, 4 do
local width = t1[draw.ID].col1 --draw.ID is got elsewhere
end
but changing the number part of .col1 to the i part so when it loops through it gets:
t1[draw.ID].col2
t1[draw.ID].col3
t1[draw.ID].col4
I'm using Lua 5.1.
for i= 2, 4 do
local width = t1[draw.ID]["col" .. i] --draw.ID is got elsewhere
end
Ideally, col would be or would contain an array-like table or sequence. This is a much more scalable way to accomplish what you're trying to do. String concatenation ['col' .. i] to access table keys in the fashion that you'd access them as an array is costly and unnecessary, if it can be avoided. This is especially important if this is something you plan to do often and want to work quickly.
-- Elements of t1 contain tables with cols.
local t1 = {}
t1[1] = {cols = {1,1,1,1}}
t1[2] = {cols = {1,1,1,1}}
t1[3] = {cols = {1,1,1,1}}
t1[4] = {cols = {1,1,1,1}}
for i=2, 4 do
local width = t1[draw.ID].cols[i]
end
-- Elements of t1 are the cols.
local t1 = {}
t1[1] = {1,1,1,1}
t1[2] = {1,1,1,1}
t1[3] = {1,1,1,1}
t1[4] = {1,1,1,1}
for i=2, 4 do
local width = t1[draw.ID][i]
end
Edit: If it's unavoidable that you have to use table keys in the style of ['col' .. i], then the best you could do is to cache them for faster access.
-- Cache all the possible keys that you'll need.
local colkeys = {}
for i=1, 30 do colkeys[i] = 'col' .. i end
for i=2, 4 do
local width = t1[draw.ID][colkeys[i]]
end
This method is anywhere from 4 to 8 times faster than concatenating a string each time that you need to index the table. It's not the ideal solution, but it works if you're stuck with the likes of col1 to col30.

how to "page" through the data in a Lua table used as a dictionary?

How would I code a function to iterate through one "pages" worth of data? Sample code would be ideal...
So say we image the size of a page is 5 items. If we had a lua table with 18 items it would need to print out:
Page 1: 1 to 5
Page 2: 6 to 10
Page 3: 11 to 15
Page 4: 16 to 18
So assume the data is something like:
local data = {}
data["dog"] = {1,2,3}
data["cat"] = {1,2,3}
data["mouse"] = {1,2,3}
data["pig"] = {1,2,3}
.
.
.
How would one code the function that would do the equivalent of this:
function printPage (myTable, pageSize, pageNum)
-- find items in "myTable"
end
So in fact I'm not even sure if a Lua table used as a dictionary can even do this? There is no specific ordering is there in such a table, so how would you be sure the order would be the same when you come back to print page 2?
The next function allows you to go through a table in an order (albeit an unpredictable one). For example:
data = { dog = "Ralf", cat = "Tiddles", fish = "Joey", tortoise = "Fred" }
function printPage(t, size, start)
local i = 0
local nextKey, nextVal = start
while i < size and nextKey ~= nil do
nextKey, nextVal = next(t, nextKey)
print(nextKey .. " = " .. nextVal)
i = i + 1
end
return nextKey
end
local nextPage = printPage(data, 2) -- Print the first page
printPage(data, 2, nextPage) -- Print the second page
I know this isn't quite in the form you were after, but I'm sure it can be adapted quite easily.
The next function returns the key after the one provided in the table, along with its value. When the end of the table is reached, it returns nil. If you provide nil as the second parameter, it returns the first key and value in the table. It's also documented in Corona, although it appears to be identical.

Resources