Is there a statement in Lua that would allow me to determinate whether it's last loop cycle?
When I'm not able to determinate how many time loop will loop?
Example:
for _, v in pairs(t) do
if I_know_this_is_your_last_cycle then
-- do something
end
This is simplified version of missingno's answer: :-)
for _, v in pairs(t) do
if next(t,_) == nil then
-- do something in last cycle
end
end
You could probably try to write something like this:
--count how many elements you have in the table
local element_cnt = 0
for k,v in pairs(t) do
element_cnt = element_cnt + 1
end
local current_item = 1
for k,v in pairs(t)
if current_item == element_count then
print "this is the last element"
end
current_item = current_item + 1
end
or this:
local last_key = nil
for k,v in pairs(t) do
last_key = k
end
for k,v in pairs(t) do
if k == last_key then
--this is the last element
end
end
In general, no. As you can see from the Lua docs, the for loop is syntax sugar for a while loop on top of an iterator so it only knows if the loop is over at the start of the loop.
If you really want to check if you are entering the last iteration then I would simply code things explicitly with an while loop.
local curr_val = initial_value
while curr_val ~= nil then
local next_val = get_next(initial_value)
if next_val ~= nil then
--last iteration
else
--not last iteration
end
curr_val = next_val
end
If you want to translate the example with the pairs function, you can use the next function as the iterator.
As an aside, I would suggest thinking twice before coding a loop like this one. The way its coded means its very easy to write code that doesn't work right when you are iterating over 0 or 1 elements or to write code that doesn't treat the last element properly. Most of the time writing a plain loop and putting the "at the end" code after the loop is more reasonable.
There are a couple of ways to do this. The easiest is to just use the standard for loop and check it yourself, like this:
local t = {5, nil, 'asdf', {'a'}, nil, 99}
for i=1, #t do
if i == #t then
-- this is the last item.
end
end
Alternatively, you could roll your own iterator function for tables that tells you when you're on the last item, something like this:
function notifyPairs(t)
local lastItem = false
local i = 0
return
function()
i = i + 1
if i == #t then lastItem = true end;
if (i <= #t) then
return lastItem, t[i]
end
end
end
for IsLastItem, value in notifyPairs(t) do
if IsLastItem then
-- do something with the last item
end
end
Related
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
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
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
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))
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.