LUA - Arrays management (add/remove elements) - arrays

I have two arrays:
A={4,8,12}
B={1,2,3,5,6,7,9,10,11}
And these code, the essence of which is, when calling the function, take a random value from array B, insert it in the order of the queue into array A, and, in another loop, remove that random value from array B.
local function Change ()
local q= math.random(1, #B)
for i = 1, #A do
if B[q]<A[i] then
for t=#A, i, -1 do
A[t+1]=A[t]
end
A[i]=B[q]
break
end
end
while q<= #B-1 do
B[q]=B[q+1]
q=q+1
end
B[q]=nil
for x = 1, #A do print ("Current: ", A[x]) end
for y = 1, #B do print ("Free: ", B[y]) end
end
Now I want to use the same code, for the inverse problem - take a random value from array A, insert it in the order of the queue into array B, and then delete the value from array A.
So I want something like this:
Change (A, B) – find random value in B and insert to A (arrays)
Change (B, A) – find random value in A and insert to B (arrays)
Is there any way in Lua to do this? Pointers or so...
P.S. I dont want to use "table.sort/table.insert", etc for this task.
P.P.S. We found solution in thread with pointer, but today I found another solution:
C={{4,8,12},{1,2,3,5,6,7,9,10,11}}
Local function Change (x,y)
a= math.random(1, #C[y])
for i = 1, #C[x] do
if C[y][a]<C[x][i] then
for t=#C[x], i, -1 do
C[x][t+1]=C[x][t]
end
C[x][i]=C[y][a]
break
end
end
while a<= #C[y]-1 do
C[y][a]=C[y][a+1]
a=a+1
end
C[y][a]=nil
for i = 1, #C[x] do print ("in C"..x, C[x][i]) end
for i = 1, #C[y] do print ("in C"..y, C[y][i]) end
end
and call Change(1,2) or Change (2,1)
How do you think, which method is better in performance?
Also I have another question in LUA syntax
When I make
C={ A={4,8,12}, B={1,2,3,5,6,7,9,10,11} }
And try inside the function #C[1] or #C[2] I got error «attempt to get length of field '?' (a nil value)» but it was very clear to write C.A or C.B instead of C[1], C[2] later in code
I had to initialize them like
A={4,8,12}, B={1,2,3,5,6,7,9,10,11}, C={A,B}
but got other error «attempt to get length of field ‘C’ (a nil value)» when use C.A or C.B.

In Lua, tables are the only objects which are shared when placed in variables.
mytable = {1,2,3}
newtable = mytable
newtable[1] = "one"
print(mytable[1]) --> prints: "one"
So in your code, we can change the hardcoded tables to variables:
local function Change(insertTable, valueTable)
local q = math.random(1, #valueTable)
for i = 1, #insertTable do
if valueTable[q] < insertTable[i] then
for t = #insertTable, i, -1 do
insertTable[t+1] = insertTable[t]
end
insertTable[i] = valueTable[q]
break
end
end
while q <= #valueTable-1 do
valueTable[q] = valueTable[q+1]
q = q+1
end
valueTable[q] = nil
for x = 1, #insertTable do print("Current: ", insertTable[x]) end
for y = 1, #valueTable do print("Free: ", valueTable[y]) end
end

Related

Select array element out of bounds on purpose

Assume the following array:
a = {"a", "b", "c"}
Using a[3], I can access c. But how do I make the string "repeat forever" (while it still only has elements)? Example:
a[4] --will return nil, but I need to to return "a", because 4 is 3 (end of array) + 1 (the element I need).
Question:
How would I make a[4] return the same value as a[1], if a[] consists of 3 elements?
You can either make sure that the key you use is within proper range or you move that logic into a's metatable by implementing the __index metamethod. That way you tell Lua what to return when someone accesses an invalid key in your table.
See http://lua-users.org/wiki/MetamethodsTutorial
local function circle(arr)
setmetatable(arr, {__index =
function(t, k)
if type(k) == "number" and #t > 0 then
return rawget(t, (k-1) % #t + 1)
end
end
})
end
local a = {"a", "b", "c"}
circle(a)
for j = -10, 10 do
print(j, a[j])
end
This is just a slight variation of Egor Skriptunoff's correct answer because I didn't want the formatting to be lost if left as comment.
In my view, this one makes the table creation a simpler single statement, and clearer as to the intended action. It also uses the same table as meta-table as a minor optimization.
local
function circle(arr)
function arr.__index(t, k)
if type(k) == 'number' and #t > 0 then
return rawget(t, (k-1) % #t + 1)
end
end
return setmetatable(arr,arr)
end
local a = circle {'a', 'b', 'c'}
for j = -10, 10 do print(j, a[j]) end

How do you return all values of an array in Lua?

Sorry if this is a dumb question, I'm a fairly inexperienced programmer.
I'm trying to return all values within an array using Lua. I can return individual elements by calling their index (ex. read_data[2]) but since the number of elements in the array is variable, I cannot simply type this out. My code:
function readformatEvent()
local read_data = {}
local duplicate
local unique_data = {}
for i=1,16 do
read_data[i] = readResult(i):readData()
end
for i=1,16 do
duplicate = 0
for j=(i+1),15 do
if read_data[i] == read_data[j] then
duplicate = 1
end
end
if duplicate == 0 then
unique_data[i] = read_data[i]
end
end
return unique_data
end
unique_data is an array consisting of unique values from the array read_data. read_data can consist of 1 to 16 elements. Being able to see the full array would help me continue to craft the code as a troubleshooting technique.
Thank you,
A short example of what you could do:
-- some example table
t = {1,2,4,5,1,2,7,8,5,4,9,3}
-- for every value v in t
for k,v in pairs(t) do
-- check v vs all remaining values in t
for m,w in pairs(t) do
-- but not vs v itself
if k ~= m then
-- remove any non unique values from t
if w == v then
t[m] = nil
t[k] = nil
end
end
end
end
-- print the result
for k,v in pairs(t) do
print(v)
end
Thanks for all the help. Here is the code that ended up working. I'm sure it's not efficient, but I'm getting the correct output now.
function readformatEvent()
function readformatEvent()
local read_data = {}
local unique_data = {}
local count = 1
local output = ""
local codes = readResult():readCount()
--create array from captured read data
for i=1,16 do
read_data[i] = readResult(i):readData()
end
--turn 2nd duplicate in the array into a nil value
for k,v in pairs(read_data) do
for m,w in pairs(read_data) do
if k ~= m then
if w == v then
read_data[m] = nil
end
end
end
end
--remove the nils from the array
for i=1,16 do
if read_data[i] ~= nil then
unique_data[count] = read_data[i]
count = count+1
end
end
--output unique values in correct format
if count == 12 and codes == 16 then
count = count - 1
else
count = count - 2
end
for i=1,count-1 do
output = output..unique_data[i]..", "
end
return output..unique_data[count]
end

Lua in pairs with same order as it's written

Is there any way to loop trough a table like the one below in the same order as it's written?
local tbl = {
["hello"] = 1,
[2] = 2,
[50] = 3,
["bye"] = 4,
[200] = 5
}
What I mean is that when I use "in pairs" I'll get a different order everytime I execute my code ...
I'm searching for something like this:
function get_keys(tbl)
local rtable = {}
for k,v in pairs(tbl) do
table.insert(rtable, k)
end
return rtable
end
local keys_of_tbl = get_keys(tbl)
for i = 1, table.getn(keys_of_tbl) do
--Do something with: tbl[keys_of_tbl[i]]
end
But because the function "get_keys" is based on "in pairs" again, it won't work ...
In Lua, the order that pairs iterates through the keys is unspecified. However you can save the order in which items are added in an array-style table and use ipairs (which has a defined order for iterating keys in an array). To help with that you can create your own ordered table using metatables so that the key order will be maintained when new keys are added.
EDIT (earlier code inserted multiple copies of the key on updates)
To do this you can use __newindex which we be called so long as the index is not added yet to the table. The ordered_add method updates, deletes, or stores the key in the hidden tables _keys and _values. Note that __newindex will always be called when we update the key too since we didn't store the value in the table but instead stored it in the "hidden" tables _keys and _values.
Note however that we cannot use any key in this table, the key name "_keys" will overwrite our hidden table so the safer alternative is to use the ordered_table.insert(t, key, value) ordered_table.index(t, key) and ordered_table.remove(t, key) methods.
ordered_table = {}
function ordered_table.insert(t, k, v)
if not rawget(t._values, k) then -- new key
t._keys[#t._keys + 1] = k
end
if v == nil then -- delete key too.
ordered_table.remove(t, k)
else -- update/store value
t._values[k] = v
end
end
local function find(t, value)
for i,v in ipairs(t) do
if v == value then
return i
end
end
end
function ordered_table.remove(t, k)
local v = t._values[k]
if v ~= nil then
table.remove(t._keys, find(t._keys, k))
t._values[k] = nil
end
return v
end
function ordered_table.index(t, k)
return rawget(t._values, k)
end
function ordered_table.pairs(t)
local i = 0
return function()
i = i + 1
local key = t._keys[i]
if key ~= nil then
return key, t._values[key]
end
end
end
function ordered_table.new(init)
init = init or {}
local t = {_keys={}, _values={}}
local n = #init
if n % 2 ~= 0 then
error"in ordered_table initialization: key is missing value"
end
for i=1,n/2 do
local k = init[i * 2 - 1]
local v = init[i * 2]
if t._values[k] ~= nil then
error("duplicate key:"..k)
end
t._keys[#t._keys + 1] = k
t._values[k] = v
end
return setmetatable(t,
{__newindex=ordered_table.insert,
__len=function(t) return #t._keys end,
__pairs=ordered_table.pairs,
__index=t._values
})
end
--- Example Usage:
local t = ordered_table.new{
"hello", 1, -- key, value pairs
2, 2,
50, 3,
"bye", 4,
200, 5
}
print(#t)
print("hello is", t.hello)
print()
for k, v in pairs(t) do --- Lua 5.2 __pairs metamethod
print(k, v)
end
t.bye = nil -- delete that
t[2] = 7 -- use integer keys
print(#t)
No. There's no "as written in the source" order to tables. (Consider that not all keys necessarily exist in the source.) lua has no concept of "in order" for non-contiguous integer keys.
If you want a specific order you get to keep that order yourself manually in some way.
If you don't have any integer keys in your table then you can use those as your order (and use ipairs to loop those keys and index the value as the key to get the real value).
If your original values are the order you want to sort in then you can loop and reverse map to get a table that you can iterate with ipairs once done.

How to remove all 'nil' values from an array?

I have an array of objects (or just numbers), and I have another array which contains all the objects that should not be removed from the first array in any circumstances. It looks something like this:
-- Array of objects (just numbers for now)
Objects = {}
-- Array of objects that should always stay in the 'Objects' array
DontDestroyThese = {}
-- Populate the arrays
Objects[#Objects+1] = 1
Objects[#Objects+1] = 2
Objects[#Objects+1] = 3
Objects[#Objects+1] = 4
Objects[#Objects+1] = 5
DontDestroyThese[#DontDestroyThese+1] = 2
DontDestroyThese[#DontDestroyThese+1] = 5
Now, I have a method called destroy() that should remove all objects from the Objects array except those included in the DontDestroyThese array. The method looks something like this:
function destroy()
for I = 1, #Objects do
if(DontDestroyThese[Objects[I]] ~= nil) then
print("Skipping " .. Objects[I])
else
Objects[I] = nil
end
end
end
However, as the result, the Objects array now contains nil values here and there. I'd like to remove these nils so that the Objects array would consist only of the numbers that were left there after calling destroy(). How do I do that?
The most efficient way is probably to create a new table to hold the result. Trying to move values around in the array is likely to have a higher overhead than simply appending to a new table:
function destroy()
local tbl = {}
for I = 1, #Objects do
if(DontDestroyThese[Objects[I]] ~= nil) then
table.insert(tbl, Objects[I])
end
end
Objects = tbl
end
This method also means you don't have to deal with altering the contents of the table/array you're iterating over.
I think the solution is much simpler. To remove any nils ('holes' in your array), all you need to do is iterate your table using pairs(). This will skip over any nils returning only the non-nil values that you add to a new local table that is returned in the end of the 'cleanup' function. Arrays (tables with indices from 1..n) will remain with the same order. For example:
function CleanNils(t)
local ans = {}
for _,v in pairs(t) do
ans[ #ans+1 ] = v
end
return ans
end
Then you simply need to do this:
Objects = CleanNils(Objects)
To test it:
function show(t)
for _,v in ipairs(t) do
print(v)
end
print(('='):rep(20))
end
t = {'a','b','c','d','e','f'}
t[4] = nil --create a 'hole' at 'd'
show(t) --> a b c
t = CleanNils(t) --remove the 'hole'
show(t) --> a b c e f
local function remove(t, pred)
for i = #t, 1, -1 do
if pred(t[i], i) then
table.remove(t, i)
end
end
return t
end
local function even(v)
return math.mod(v, 2) == 0
end
-- remove only even numbers
local t = remove({1, 2, 3, 4}, even)
-- remove values you want
local function keep(t)
return function(v)
return not t[v]
end
end
remove(Objects, keep(DontDestroyThese))

Can't declare table element with number from another table

Code is very simple. Declaring two tables, a simple and a multi-dimensional:
Player = {X_Pos = 1, Y_Pos = 1, Current_Sprite_Num = 100}
for j=1, Max_col_length do -- value ofMax_col_length doesn't matter here; positive integer anyway
MapLayer_B[j] = {}
for i=1, Max_row_length do --same here
MapLayer_B[j][i] = 1
end
end
Then I try to do this operation:
MapLayer_B[Player[X_Pos]][Player[Y_Pos]] = Player[Current_Sprite_Num]
It should replace the Player[X_Pos]th element of the Player[Y_Pos]th rowtable in MapLayer_B. Instead, I got this error with LÖVE compiler:
Error: attempt to index field '?' (a nil value)
I don't really get, why it happens, since all elements of both MapLayer_B andPlayer tables are declared and not kept in nil.
Any ideas?
You need to use Player.X_Pos instead of Player[X_Pos] and so on.
The brackets notation will interpret "X_Pos" as a variable and try to access taht key instead (the reason for the error is that undefined variables default to null)
t = {a = 17}
print( t.a ) --dot notation is simpler
print( t["a"] ) --bracket notation expects a string
key = "a" --that string can be from a variable
print( t[key] )

Resources