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

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))

Related

LUA - Arrays management (add/remove elements)

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

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 doesn't go over 'nil' in table

I have such an anonymous Lua function:
an_func = function(x) return x == nil end
I need to test it on a table from 4 values: {'None', nil, 1, '1'}.
So, I wrote the following code:
for num, element in pairs({'None', nil, 1, '1'}) do
print(num .. ': ' .. tostring(an_func(element)))
end
When I ran it, I got only three lines:
1: false
3: false
4: false
The line with the true value (corresponding to nil table element) didn't print.
Can you explain, why did Lua print all results, but true? How can I fix the snipped make it output all 4 lines?
P.S. I'm newbee in Lua.
When using pairs(), it goes over all elements of a table. nil means nothing, so it won't go over it. Why should it tell you there's nothing in the table? If you have a totally new table, it'll return nil if you index it with anything. print(({}).test) --> nil
If you really want to get the nil, you could use a dummy value:
local NIL = setmetatable({},{__tostring=function() return "nil" end})
an_func = function(x) return x == NIL end
for num, element in pairs({'None', NIL, 1, '1'}) do
print(num .. ': ' .. tostring(an_func(element)))
end
Output:
1: false
2: true
3: false
4: false
In Lua, everything (a function, string, thread, ...) is a value, with nil (and technically none/void) being the only exception(s). To put simply, nil represents the absence of value. So when iterating over a table, why would Lua say there is nothing there. If it did it would have to iterate over every virtually possible index you can create.
However it appears you are using an array. You technically can iterate over it assuming you know the length. I'll make the assumption that the last item in the table is NOT nil, and that there wasn't any holes assigned after the initial length of the table with holes, however this may not necessarily be true in your case, if it isn't you will have to keep track of the length yourself and use it rather than the # operator:
for i = 1,#t do
local v = t[i]
print (v == nil)
end
Other answers address your second question well but the simple explanatory facts are Any key with value nil is not considered part of the table. Conversely, any key that is not part of a table has an associated value nil. Assigning a value of nil removes any existing key-value pair with the same key.
You would need to loop through all keys you want to print the values for.
Possible ways:
Know the length beforehand. (warspyking noted about this)
Using an end marker.
Calculate the length somehow (for example with maxn).
Use something to represent nil values. (EinsteinK showed an example
of this)
Each has their good and bad sides, here are some:
For the first you would need to know the length beforehand in all cases which may be difficult.
The end marker is convenient, but requires you to shift around the end marker.
Calculating length would not work if the last element is nil.
Example of calculating the length - assuming last element is not nil.
Note that lua 5.1 has table.maxn that you can use instead of this custom implementation of maxn.
an_func = function(x) return x == nil end
function maxn(t)
local n = 0
for k,v in pairs(t) do
if k%1 == 0 then
n = k
end
end
return n
end
local t = {'None', nil, 1, '1'}
for num = 1, maxn(t) do
print(num .. ': ' .. tostring(an_func(t[num])))
end
Example of using an end marker:
an_func = function(x) return x == nil end
local END = {}
local t = {'None', nil, 1, '1', END}
local num = 1
while t[num] ~= END do
print(num .. ': ' .. tostring(an_func(t[num])))
num = num+1
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 can I check if a lua table contains only sequential numeric indices?

How can I write a function that determines whether it's table argument is a true array?
isArray({1, 2, 4, 8, 16}) -> true
isArray({1, "two", 3, 4, 5}) -> true
isArray({1, [3]="two", [2]=3, 4, 5}) -> true
isArray({1, dictionaryKey = "not an array", 3, 4, 5}) -> false
I can't see any way of finding out if the numeric keys are the only keys.
EDIT: Here's a new way to test for arrays that I discovered just recently. For each element returned by pairs, it simply checks that the nth item on it is not nil. As far as I know, this is the fastest and most elegant way to test for array-ness.
local function isArray(t)
local i = 0
for _ in pairs(t) do
i = i + 1
if t[i] == nil then return false end
end
return true
end
ipairs iterates over indices 1..n, where n+1 is the first integer index with a nil value
pairs iterates over all keys.
if there are more keys than there are sequential indices, then it cannot be an array.
So all you have to do is see if the number of elements in pairs(table) is equal to the number of elements in ipairs(table)
the code can be written as follows:
function isArray(tbl)
local numKeys = 0
for _, _ in pairs(tbl) do
numKeys = numKeys+1
end
local numIndices = 0
for _, _ in ipairs(tbl) do
numIndices = numIndices+1
end
return numKeys == numIndices
end
I'm pretty new to Lua, so there might be some builtin function to reduce the numKeys and numIndices calculations to simple function calls.
By "true array", I suppose you mean a table whose keys are only numbers. To do this, check the type of every key of your table. Try this :
function isArray(array)
for k, _ in pairs(array) do
if type(k) ~= "number" then
return false
end
end
return true --Found nothing but numbers !
end
Note: as #eric points out, pairs is not defined to iterate in a specific order. Hence this is no valid answer.
The following should be sufficient; it checks that the keys are sequential from 1 until the end:
local function isArray(array)
local n = 1
for k, _ in pairs(array) do
if k ~= n then return false end
n = n + 1
end
return true
end
I wrote this code for another similar question lately:
---Checks if a table is used as an array. That is: the keys start with one and are sequential numbers
-- #param t table
-- #return nil,error string if t is not a table
-- #return true/false if t is an array/isn't an array
-- NOTE: it returns true for an empty table
function isArray(t)
if type(t)~="table" then return nil,"Argument is not a table! It is: "..type(t) end
--check if all the table keys are numerical and count their number
local count=0
for k,v in pairs(t) do
if type(k)~="number" then return false else count=count+1 end
end
--all keys are numerical. now let's see if they are sequential and start with 1
for i=1,count do
--Hint: the VALUE might be "nil", in that case "not t[i]" isn't enough, that's why we check the type
if not t[i] and type(t[i])~="nil" then return false end
end
return true
end
Here's my take on this, using #array to detect a gap or stop when too many keys have been read:
function isArray(array)
local count=0
for k,_ in pairs(array) do
count=count+1
if (type(k) ~= "number" or k < 1 or k > #array or count > #array or math.floor(k) ~= k) then
return false
end
end
if count ~= #array then
return false
end
return true
end
Iterate from 0 to the number of elements, and check if all elements with the counter's index exist. If it's not an array, some indexes will miss in the sequence.

Resources