How to table.insert(x) from table.remove(x) - lua

I want to manipulate a lua table by adding and removing elements.
I would use table.remove(x), store (x) into an array and then insert it back with table.insert(x).
a = {}
table.remove(tab, a) -- From tab into a
...
table.insert(tab, a) -- From a into tab
That won't work.

Simply because that is not how they work.
Please refer to table.insert and table.remove of Lua Reference Manual. This one is for Lua 5.1, if you use different version then you should be able to easily find proper one.
You can move elements between tables like this:
a = {}
table.insert(a, table.remove(tab)) -- From tab into a
...
table.insert(tab, table.remove(a)) -- From a into tab
You may need to verify value returned by remove:
local foo = table.remove(tab)
if type(foo) ~= "nil" then
table.insert(a, foo)
end
Note that default position for remove is the last element of the table and default position for insert is after last element of the table. Manipulating pos argument of either insert or remove may allow you to implement different behaviours.
For instance if you would like to get more scrolling-like behaviour (the one you asked for in your previous, now deleted, question):
a = {}
local foo = table.remove(tab, 1)
if type(foo) ~= "nil" then table.insert(a, foo) end -- From tab into a
...
local bar = table.remove(a, 1)
if type(bar) ~= "nil" then table.insert(tab, bar) end -- From a into tab

Related

Lua overloads the operator to create a fake array

I want to create a 0-memory lua array that actually jumps to my custom function when I use operators like # [] on it
Any ideas on how to do this?
I want the user using this fake array to not perceive it as fake, it is worse than a normal array in terms of access speed, but has better memory performance
Lua implements something called metamethods (documentation)
Metamethods are functions which exist subsequently to a table and fire on certain operations such as indexing the array, reading missing indices, gathering the length of the array, or even math operations such as + - * /
-- Start by creating your array, and another for demonstration purposes
local object = {}
local demo = {1, 2, 3, 4}
-- Create a your metamethods contained in a table
local metamethods = {
__index = function(self, index) return demo[index] end;
__newindex = function(self, index, value) demo[index] = value end;
}
-- Lets print out what is in the object table for demonstration
print(object[1]) -- nil
print(object[2]) -- nil
print(object[3]) -- nil
print(object[4]) -- nil
-- Use the setmetatable(table a, table b) function to set the metamethods
-- stored in 'b' to 'a'
setmetatable(object, metamethods);
-- Lets print out what is in the object table for demonstration
print(object[1]) -- 1
print(object[2]) -- 2
print(object[3]) -- 3
print(object[4]) -- 4
Why does the above code work? When a metatable is set with the index __index (metamethods.__index), if the attached table's (object) is indexed and the key isn't present (nil), then it will call the specified function. In the __index function, it returns the demo's table with the index passed straight to it. So its as if: when you do object[1], you actually do demo[1] but with a metamethod's help of course. This effectively creates a proxy of sorts.
One cool and quick usage of setmetatable() is that it returns the value you pass as the first parameter (a table).
local object1 = setmetatable({}, { __index = function(self, i) return 1 end })
print(object1["a"]) -- 1
print(object2[321]) -- 1

How can i use table.insert without it inserting the index?

local t={}
for i,v in pairs(game.Players:GetPlayers()) do
if v~=game.Players.LocalPlayer then
table.insert(t,v.Name)
end
end
if i do table.foreach(t,print) it also shows the index and it causes problems when i want to select a random player. The error usually tends to be something like "1 is not a valid member of workspace"
Your problem is not that table.insert adds an index, but that you use it instead of the value you want do use.
table.insert inserts an element to a list. If no index is given the value is added to the end of the list.
So there is no way to use table.insert without "inserting the index". Actually there is no way at all to add an element to a Lua table without providing a key. A Lua table is nothing but a set of key value pairs.
table.foreach calls a function for each key value pair.
You should rather use table.foreachi btw.
So table.foreachi(t, print) is equivalent to
print(1, t[1])
print(2, t[2])
...
If you just want to print the values:
for i,v in ipairs(t) do print(v) end
or for older Lua versions
table.foreachi(t, function(i,v) print(v) end)
Alternatively you run a numeric for loop:
for i = 1, t.n do print(t[i]) end
or
for i = 1, #t do print(t[i]) end
table.foreach(t,f) applies the function f to each key-value pair of t as is written. Also, since Lua 5.1 the function is deprecated.
If you want simply to print all values, do
for _, name in ipairs (t) do
print (name)
end
or
print (table.concat (t, ', '))
If you want a random name from t:
local random_name = t [math.random (#t)]

"table expected, got string" when nesting tables and attempting table.remove

I'm currently attempting to create a table of tables, and removing parts from the previous nested table to make the next nested table, thereby decreasing the length of each nested table by 1.
However, upon running the code below, it triggers a bad argument #1 to 'remove' (got string, expected table) error. I can't understand why this is.
possiblePorts = {}
possiblePorts[1] = {"VGA","USB","Ethernet","9mm","HDMI"}
for i=2,5 do
possiblePorts[i] = table.remove(possiblePorts[i-1],math.random(1,5))
end
I'm expecting it to create a table of:
possiblePorts = {
{"VGA","USB","Ethernet","9mm","HDMI"},
{"VGA","Ethernet","9mm","HDMI"},
{"VGA","9mm","HDMI"},
{"9mm","HDMI"},
{"9mm"}
} --formatted for simple viewing
or something similar - why is it not doing so, and what can I do to fix it?
table.remove will return the removed element not the remaining elements of the table.
Lua 5.3 Reference Manual #table.remove
What happens in your code is the first loop works with no issues.
During the second loop, possiblePorts[i-1] is now 2 so we attempt to use table.remove on the value at index 2. The value we put at index 2, in the first loop, was a string so we generate the error trying to pass it as the first arg of table.remove.
You also cannot use math.random(1,5) on each table as that gives you a risk of hitting outside the end of the array, and this will result in an error from table.remove. You want to change 5 out for the length of the array.
This code does what you were trying to accomplish
local possiblePorts = {}
possiblePorts[1] = {"VGA","USB","Ethernet","9mm","HDMI"}
for i=2,5 do
possiblePorts[i] = {}
local skip = math.random(1,#possiblePorts[i-1]) -- Get value we will skip at random
local index = 0 -- Index for new array
for j=1,#possiblePorts[i-1] do -- Loop over all the elements of that last array.
if j ~= skip then -- If the value is not the one we are skipping add it.
index = index + 1
possiblePorts[i][index] = possiblePorts[i-1][j]
end
end
end
for k,v in ipairs(possiblePorts) do
print(k, "{" .. table.concat(v," ") .. "}")
end
Output:
1 {VGA USB Ethernet 9mm HDMI}
2 {USB Ethernet 9mm HDMI}
3 {USB Ethernet HDMI}
4 {Ethernet HDMI}
5 {Ethernet}

How to create a dynamic matrix in lua

I'm trying to work with some matrix in lua for my dungeon generator. Basically I'll have matrix [x][y] and a structure inside there will store the info of each "room". But since it is a generator, I don't know how many rooms I'll have, and the only way I know is to make something like this:
mat = {}
for i = 0, 10 do
mat[i] = {}
for j = 0, 10 do
mat[i][j] = 1
end
end
So the question is, is there a way to create a matrix that dynamic increases the size as I add data to it? because there will be blank spaces since the dungeon is going to be like a tree branch.
From Programming in Lua:
Moreover, tables have no fixed size; you can add as many elements as
you want to a table dynamically.
To handle access to non-existent table members and so avoid error messages for indexing nil values you can use a metatable implementing the __index metamethod.
In the following example Lua will insert an empty table into your table whenever it is not there yet.
Please refer to https://www.lua.org/manual/5.3/manual.html#2.4 for details
local mt_2D = {
__index =
function(t, k)
local inner = {}
rawset(t, k, inner)
return inner
end
}
local array2D = setmetatable({}, mt_2D)
array2D[2][5] = 'it works'
print(array2D[2][5]) --> it works

Lua : copying a table efficiently (deep copy)

I try to make efficiently a copy of a lua table. I have written the following function copyTable() that works well (see below). But I imagined I could have something more efficient using the "passing by value" mechanism of the functions. I made a few tests to explore this mechanism :
function nop(x)
return x
end
function noop(x)
x={}
return x
end
function nooop(x)
x[#x+1]=4
return x
end
function copyTable(datatable)
local tblRes={}
if type(datatable)=="table" then
for k,v in pairs(datatable) do tblRes[k]=copyTable(v) end
else
tblRes=datatable
end
return tblRes
end
tab={1,2,3}
print(tab) -->table: 0x1d387e0 tab={1,2,3}
print(nop(tab)) -->table: 0x1d387e0 tab={1,2,3}
print(noop(tab)) -->table: 0x1e76f90 tab={1,2,3}
print(nooop(tab)) -->table: 0x1d387e0 tab={1,2,3,4}
print(tab) -->table: 0x1d387e0 tab={1,2,3,4}
print(copyTable(tab)) -->table: 0x1d388d0
We can see that the reference to the table is transferred unchanged through the functions (when I just read it or add things) except within noop() where I try a radical modification of the existing.
I read Bas Bossink and the answer made by Michael Anderson in this Q/A. Regarding the passing or tables as arguments, they emphasized the difference between "arguments passed by ref" and "arguments passed by values and tables are references" with examples where this difference appears.
But what does that mean precisely ? Do we have a copy of the reference, but what difference does that make with a passing through ref since the data pointed and therefore manipulated is still the same, not copied ? Is the mechanism in noop() specific when we try to affect nil to the table, specific to avoid the deletion of the table or in which cases does it trigger (we can see with nooop() that it is not always the case when the table is modified) ?
My question : how the mechanism of passing tables really works ? Is there a way to make a more efficient way to copy the data of a table without the burden of my copyTable ?
The rules of argument passing in Lua is similarly to C: everything is pass by value, but tables and userdata are passed around as pointers. Passing a copy of a reference does not appear so different in usage, but it is completely different than passing by reference.
For example, you brought this part up specifically.
function noop(x)
x={}
return x
end
print(noop(tab)) -->table: 0x1e76f90 tab={1, 2, 3}
You are assigning the value for the new table[1] into variable x (x now holds a new pointer value). You didn't mutate the original table, the tab variable still holds the pointer value to the original table. When you return from noop you are passing back the value of the new table, which is empty. Variables hold values, and a pointer is a value, not a reference.
Edit:
Missed your other question. No, if you want to deep-copy a table, a function similar to what you wrote is the only way. Deep copies are very slow when tables get large. To avoid performance issues, you might use a mechanism like "rewind tables", which keep track of changes made to them so they can be undone at later points in time (very useful in recursive with backtrack contexts). Or if you just need to keep users from screwing with table internals, write a "freezable" trait.
[1] Imagine the {} syntax is a function that constructs a new table and returns a pointer to the new table.
If you are sure that those 3 assumptions (A) are valid for "tab" (the table being copied):
There are no table keys
t1 = {}
tab = {}
tab[t1] = value
There are no repeated table values
t1 = {}
tab = {}
tab.a = t1
tab.b = t1
-- or
-- tab.a.b...x = t1
There are no recursive tables:
tab = {}
tab.a = tab
-- or
-- tab.a.b...x = tab
Then the code you provided is the smallest and almost as efficient as possible.
If A1 doesn't hold (i.e. you have table keys), then you must change your code to:
function copyTable(datatable)
local tblRes={}
if type(datatable)=="table" then
for k,v in pairs(datatable) do
tblRes[copyTable(k)] = copyTable(v)
end
else
tblRes=datatable
end
return tblRes
end
If A2 doesn't hold (i.e. you have repeated table values), then you could change your code to:
function copyTable(datatable, cache)
cache = cache or {}
local tblRes={}
if type(datatable)=="table" then
if cache[datatable] then return cache[datatable]
for k,v in pairs(datatable) do
tblRes[copyTable(k, cache)] = copyTable(v, cache)
end
cache[datatable] = tblRes
else
tblRes=datatable
end
return tblRes
end
This approach only pays off, though, if you have lots of repeated large tables. So, it is a matter of evaluating which version is faster for your actual production scenario.
If A3 doesn't hold (i.e. you have recursive tables), then your code (and both adjustments above) will enter an infinite recursive loop and eventually throw a stack overflow.
The simplest way to handle that is keeping a backtrack and throwing an error if table recursion happens:
function copyTable(datatable, cache, parents)
cache = cache or {}
parents = parents or {}
local tblRes={}
if type(datatable)=="table" then
if cache[datatable] then return cache[datatable]
assert(not parents[datatable])
parents[datatable] = true
for k,v in pairs(datatable) do
tblRes[copyTable(k, cache, parents)]
= copyTable(v, cache, parents)
end
parents[datatable] = false
cache[datatable] = tblRes
else
tblRes=datatable
end
return tblRes
end
My solution for a deepcopy function which handles recursive tables, preserving the original structure may be found here: https://gist.github.com/cpeosphoros/0aa286c6b39c1e452d9aa15d7537ac95

Resources