For loop doesn't go in order (ipairs doesn't work)

I’m trying to loop through a Zombie Spawning Queue table, but for some reason it will stop looping once there are 10 values left in the table. I don’t think it loops in order either, even with ipairs, because a value was removed from around the middle of the table.

Once I realized I had to remove the zombie from the queue table every time since it got spawned, I used table.remove(Global_Info.ZombieQueue, i). Before this, it worked completely fine (besides the fact that I forgot to remove the zombie from the zombie queue once it spawned of course), and I removed the line table.remove(Global_Info.ZombieQueue, i) to prove this, and it looped through the entire table, so the problem has something to do with removing zombies from the table.

I also tried using a while true do loop instead, using a counter variable to track what index to use, but that didn’t work either.

Here’s the spawning function:

--Constant spawning of zombies
local function ZombieSpawning()
	
	--VARIABLES--
	
	--LOGIC--
	
	--Loop through zombie queue and spawn zombies
	for i, zombieName in ipairs(Global_Info.ZombieQueue) do
		
		--Make sure the function should run
		if Global_Info.RoundInfo.Active == false or not CurrentMapFolder:FindFirstChildWhichIsA("Model") or not CurrentMapFolder:FindFirstChildWhichIsA("Model"):FindFirstChild("ZombieSpawns") then return end
		
		--VARIABLES--
		
		local CurrentMap = CurrentMapFolder:FindFirstChildWhichIsA("Model")
		local ZombieSpawns = CurrentMap:FindFirstChild("ZombieSpawns")
		
		--LOGIC--
		
		--Spawn zombie	
		ZombieSpawnModule.IndividualSpawn(ZombieInfoModule.Info[zombieName], ZombieSpawns:GetChildren()[math.random(#ZombieSpawns:GetChildren())], Global_Info.Entrances)
		table.remove(Global_Info.ZombieQueue, i)
		
		--Cooldown
		task.wait(Global_Info.RoundInfo.ZombieSpawnCooldown)
		
	end
	
end

(If you need to know other code in scripts let me know)

Thanks :happy2:

ipairs stops iterating over the array when it encounteres a nil value, so if the for loop stops after the 10th iteration, it indicates that the 11th value stored at Global_Info.ZombieQueue is missing

The 11th value being nil can explain why using a while loop with a counter also didn’t work, since if the code running in the loop depends on there being a value (which it does since you are using zombieName), an error would occur which will stop the loop from continuing to iterate over the array

Essentially, this means that to fix your issue, you’ll need to investigate why the 11th value of the Global_Info.ZombieQueue array isn’t being set properly (or at all)


@Void_Trader I’ve just noticed that you’re removing values from the array while iterating over the same array:

I don’t recommend you do so, as it can cause problems to occur, and could be the cause of your issue. In your case, since you seem to be removing all of the array’s values, using table.clear after the for loop could fix the problem:

--Constant spawning of zombies
local function ZombieSpawning()

	--VARIABLES--

	--LOGIC--

	--Loop through zombie queue and spawn zombies
	for i, zombieName in ipairs(Global_Info.ZombieQueue) do

		--Make sure the function should run
		if Global_Info.RoundInfo.Active == false or not CurrentMapFolder:FindFirstChildWhichIsA("Model") or not CurrentMapFolder:FindFirstChildWhichIsA("Model"):FindFirstChild("ZombieSpawns") then return end

		--VARIABLES--

		local CurrentMap = CurrentMapFolder:FindFirstChildWhichIsA("Model")
		local ZombieSpawns = CurrentMap:FindFirstChild("ZombieSpawns")

		--LOGIC--

		--Spawn zombie	
		ZombieSpawnModule.IndividualSpawn(ZombieInfoModule.Info[zombieName], ZombieSpawns:GetChildren()[math.random(#ZombieSpawns:GetChildren())], Global_Info.Entrances)

		--Cooldown
		task.wait(Global_Info.RoundInfo.ZombieSpawnCooldown)

	end

	table.clear(Global_Info.ZombieQueue) -- This will remove all values from the array, but will keep its allocated capacity
	-- Global_Info.ZombieQueue = {} -- Alternatively, you can set the array to an empty table to completely reset it

end
2 Likes

Is there a way to use the normal in pairs() to counter this problem instead? If not, this will work, but it would be easier to work with if the zombie got removed from the zombie queue immediately after it spawns, without breaking the loop.

I also plan on being able to add zombies to the zombie queue in real time, meaning I can add zombies to the zombie queue whenever and it will be added to that loop. Is that possible?

Thanks for helping

1 Like

Using pairs seems to work well when I test it, it’s iterating over the whole array even though the value is being removed, so that was a good idea :slight_smile::+1:


@Void_Trader The statement above is invalid because I’ve found the true cause of the issues you’re experiencing: You’re using table.remove to remove the value from the array (while iterating over the same array). The reason why I said what I said is becaused I accidentally used a different way to remove the value from the array within my test, which is: array[index] = nil, and it worked exactly as you desire: The for loop successfully iterated over the whole array, both when using ipairs or pairs, and printing the contents of the array after the for loop shows that it was completely emptied. You can run the following tests in a new baseplate to confirm what I’m saying:

Test 1:

local array = {3, 6, 9, 12, 15}

for i, v in ipairs(array) do
	print(i, v) -- Doesn't print value 4 and 5
	table.remove(array, i)
end

warn(array) -- Array wasn't emptied correctly, value 1 and 2 still exist

Test 2:

local array = {3, 6, 9, 12, 15}

for i, v in ipairs(array) do
	print(i, v) -- Successfully prints all values
	array[i] = nil
end

warn(array) -- Array was emptied correctly
1 Like