Array problem

I have an array containing display objects. I’m looping through the array each frame and moving the objects across the screen. When they reach the far side of the screen, I remove the object from the array, and remove it from the display. 

Here’s problem, the next frame I get an error: “attempt to index local ‘thing’ (a nil value)”. It’s like the array still has an index reserved for the object that has been removed. 

I’m using the following to remove object from the array: 

table.remove( thing\_array, table.indexOf( thing\_array, thing ) )

What am I missing here?

Here’s a full sample of code you can copy and paste this into a landscape project to test. 

-- Define an array to hold things local thing\_array = {} local function list\_things() local str = "" for i = 1, #thing\_array do str = str .. thing\_array[i].name .. " " end print( str ) end -- Define a function to remove things local function remove\_thing( thing ) print( thing.name, table.indexOf( thing\_array, thing ), #thing\_array ) table.remove( thing\_array, table.indexOf( thing\_array, thing ) ) display.remove( thing ) end -- Define a function to make things local function make\_thing() local thing = display.newCircle( 0, 0, 24 ) thing.x = -60 thing.y = 60 thing.speed\_x = 3 thing.vy = 0 thing.gravity = 1 thing.name = "thing\_" .. #thing\_array table.insert( thing\_array, thing ) end -- This function handles enter frame events local function on\_frame( event ) list\_things() for i = 1, #thing\_array do local thing = thing\_array[i] thing.vy = thing.vy + thing.gravity thing.y = thing.y + thing.vy if thing.y \> 300 then thing.y = 300 thing.vy = -thing.vy end thing.x = thing.x + thing.speed\_x if thing.x \> 540 then remove\_thing( thing ) end end end Runtime:addEventListener( "enterFrame", on\_frame ) local thing\_timer = timer.performWithDelay( 1000, make\_thing, 0 )
-- Define an array to hold things local thing\_array = {} local function list\_things()     local str = ""     for i = 1, #thing\_array do         str = str .. thing\_array[i].name .. " "     end     print( str ) end -- Define a function to remove things local function remove\_thing( thing )     print( thing.name, table.indexOf( thing\_array, thing ), #thing\_array )          table.remove( thing\_array, table.indexOf( thing\_array, thing ) )          display.remove( thing ) end -- Define a function to make things local function make\_thing()     local thing = display.newCircle( 0, 0, 24 )     thing.x = -60     thing.y = 60          thing.speed\_x = 3     thing.vy = 0     thing.gravity = 1          thing.name = "thing\_" .. #thing\_array          table.insert( thing\_array, thing ) end -- This function handles enter frame events local function on\_frame( event )     list\_things()     testing = false     for i = 1, #thing\_array do if testing == true then return true else         local thing = thing\_array[i]                  thing.vy = thing.vy + thing.gravity         thing.y = thing.y + thing.vy         if thing.y \> 300 then             thing.y = 300             thing.vy = -thing.vy         end                  thing.x = thing.x + thing.speed\_x         if thing.x \> 540 then             remove\_thing( thing )             testing = true         end     end end end Runtime:addEventListener( "enterFrame", on\_frame ) local thing\_timer = timer.performWithDelay( 1000, make\_thing, 0 )

All I added was testing = true when it removes an item so that it kills the for loop and no more errors =) your coding was correct

Thanks for the reply. I also found a solution. After a little research, my problem lies in the fact that Lua checks the count of a loop only at the initialization of the loop. So if the number of items in the array changes while the loop is running there is potential for trouble. 

A better solution, and this seems to be the way Lua is meant to handle these things is with ipairs(). This function sets up an iterator which will loop through all items in the array once. Here’s my solution. 

-- Define some variables local MAX\_GRAVITY = 4 local MIN\_GRAVITY = 1 local MIN\_SPEED = 2 local MAX\_SPEED = 7 local THING\_FREQUENCY = 1500 -- Define an array to hold things local thing\_array = {} -- Define a function to remove things local function remove\_thing( thing ) print( thing.name, table.indexOf( thing\_array, thing ), #thing\_array ) table.remove( thing\_array, table.indexOf( thing\_array, thing ) ) display.remove( thing ) end -- Define a function to make things local function make\_thing() local thing = display.newCircle( 0, 0, 24 ) thing.x = -60 thing.y = 60 thing.speed\_x = ( math.random() \* MAX\_SPEED ) + MIN\_SPEED thing.vy = 0 thing.gravity = ( math.random() \* MAX\_GRAVITY ) + MIN\_GRAVITY thing.name = "thing\_" .. #thing\_array table.insert( thing\_array, thing ) end -- This function handles enter frame events local function on\_frame( event ) for i, thing in ipairs( thing\_array ) do thing.vy = thing.vy + thing.gravity thing.y = thing.y + thing.vy if thing.y \> 300 then thing.y = 300 thing.vy = -thing.vy end thing.x = thing.x + thing.speed\_x if thing.x \> 540 then remove\_thing( thing ) end end end Runtime:addEventListener( "enterFrame", on\_frame ) local thing\_timer = timer.performWithDelay( THING\_FREQUENCY, make\_thing, 0 )

@soggybag,

Can I suggest this instead…

1., Insert to table like this:

thing\_array[thing] = thing 
  1. Remove like this:

    thing_array[thing] = nil 

i.e. Don’t use integer indexes, use the objects themselves to index the array.

  1. You can still iterate over the array at any time like this:

    for k,v in pairs( thing_array ) do    – Do something with v here end 

** Update ** 

I see you have some code to make a unique name, using the thing index.  You can do that like this instead:

thing.name = "thing\_" .. tostring(thing) 

Thanks for the reply. I hit on the idea of using the ipair() iterator. This seems to make a lot of sense that you are meant to use the built in iterator.

I curious as why using the object itself as the key would be better than using an integer key? Seems the key would be easier to remove the item by key name. What about performance does having many keys set to nil cause a problem?

  1. Using integer and non-integer keys and direct assignment is faster than using the table.insert()

    thing_array[thing] = thing – Faster than: table.insert( thing_array, thing ) 

  2. Using integer and non-integer keys and nil assignment is faster than table.remove()

    thing_array[thing] = nil – Faster than: table.remove( thing_array, table.indexOf( thing_array, thing ) ) 

  3.  You store values, not keys, so assigning a nil to a value effectively removes that storage (not entirely true nor simple to explain.  Read more about Lua garbage collection to understand how tables clean up).

    thing_array[10] = thing – storing thing at index 10 thing_array[10] = nil – removing thing from index 10 

What about performance?  I would suggest you not worry about it too much. Choose good practices over tricky optimizations.  As far as integer vs non-integer indexes?  The difference is negligible in most uses.  I you are doing 10’s and 100’s of thousands of indexes per frame then you might have to thing about it.

-- Define an array to hold things local thing\_array = {} local function list\_things()     local str = ""     for i = 1, #thing\_array do         str = str .. thing\_array[i].name .. " "     end     print( str ) end -- Define a function to remove things local function remove\_thing( thing )     print( thing.name, table.indexOf( thing\_array, thing ), #thing\_array )          table.remove( thing\_array, table.indexOf( thing\_array, thing ) )          display.remove( thing ) end -- Define a function to make things local function make\_thing()     local thing = display.newCircle( 0, 0, 24 )     thing.x = -60     thing.y = 60          thing.speed\_x = 3     thing.vy = 0     thing.gravity = 1          thing.name = "thing\_" .. #thing\_array          table.insert( thing\_array, thing ) end -- This function handles enter frame events local function on\_frame( event )     list\_things()     testing = false     for i = 1, #thing\_array do if testing == true then return true else         local thing = thing\_array[i]                  thing.vy = thing.vy + thing.gravity         thing.y = thing.y + thing.vy         if thing.y \> 300 then             thing.y = 300             thing.vy = -thing.vy         end                  thing.x = thing.x + thing.speed\_x         if thing.x \> 540 then             remove\_thing( thing )             testing = true         end     end end end Runtime:addEventListener( "enterFrame", on\_frame ) local thing\_timer = timer.performWithDelay( 1000, make\_thing, 0 )

All I added was testing = true when it removes an item so that it kills the for loop and no more errors =) your coding was correct

Thanks for the reply. I also found a solution. After a little research, my problem lies in the fact that Lua checks the count of a loop only at the initialization of the loop. So if the number of items in the array changes while the loop is running there is potential for trouble. 

A better solution, and this seems to be the way Lua is meant to handle these things is with ipairs(). This function sets up an iterator which will loop through all items in the array once. Here’s my solution. 

-- Define some variables local MAX\_GRAVITY = 4 local MIN\_GRAVITY = 1 local MIN\_SPEED = 2 local MAX\_SPEED = 7 local THING\_FREQUENCY = 1500 -- Define an array to hold things local thing\_array = {} -- Define a function to remove things local function remove\_thing( thing ) print( thing.name, table.indexOf( thing\_array, thing ), #thing\_array ) table.remove( thing\_array, table.indexOf( thing\_array, thing ) ) display.remove( thing ) end -- Define a function to make things local function make\_thing() local thing = display.newCircle( 0, 0, 24 ) thing.x = -60 thing.y = 60 thing.speed\_x = ( math.random() \* MAX\_SPEED ) + MIN\_SPEED thing.vy = 0 thing.gravity = ( math.random() \* MAX\_GRAVITY ) + MIN\_GRAVITY thing.name = "thing\_" .. #thing\_array table.insert( thing\_array, thing ) end -- This function handles enter frame events local function on\_frame( event ) for i, thing in ipairs( thing\_array ) do thing.vy = thing.vy + thing.gravity thing.y = thing.y + thing.vy if thing.y \> 300 then thing.y = 300 thing.vy = -thing.vy end thing.x = thing.x + thing.speed\_x if thing.x \> 540 then remove\_thing( thing ) end end end Runtime:addEventListener( "enterFrame", on\_frame ) local thing\_timer = timer.performWithDelay( THING\_FREQUENCY, make\_thing, 0 )

@soggybag,

Can I suggest this instead…

1., Insert to table like this:

thing\_array[thing] = thing 
  1. Remove like this:

    thing_array[thing] = nil 

i.e. Don’t use integer indexes, use the objects themselves to index the array.

  1. You can still iterate over the array at any time like this:

    for k,v in pairs( thing_array ) do    – Do something with v here end 

** Update ** 

I see you have some code to make a unique name, using the thing index.  You can do that like this instead:

thing.name = "thing\_" .. tostring(thing) 

Thanks for the reply. I hit on the idea of using the ipair() iterator. This seems to make a lot of sense that you are meant to use the built in iterator.

I curious as why using the object itself as the key would be better than using an integer key? Seems the key would be easier to remove the item by key name. What about performance does having many keys set to nil cause a problem?

  1. Using integer and non-integer keys and direct assignment is faster than using the table.insert()

    thing_array[thing] = thing – Faster than: table.insert( thing_array, thing ) 

  2. Using integer and non-integer keys and nil assignment is faster than table.remove()

    thing_array[thing] = nil – Faster than: table.remove( thing_array, table.indexOf( thing_array, thing ) ) 

  3.  You store values, not keys, so assigning a nil to a value effectively removes that storage (not entirely true nor simple to explain.  Read more about Lua garbage collection to understand how tables clean up).

    thing_array[10] = thing – storing thing at index 10 thing_array[10] = nil – removing thing from index 10 

What about performance?  I would suggest you not worry about it too much. Choose good practices over tricky optimizations.  As far as integer vs non-integer indexes?  The difference is negligible in most uses.  I you are doing 10’s and 100’s of thousands of indexes per frame then you might have to thing about it.