Remove Objects Safely from Table

Summary

I been use the table.remove() to get rid of objects stored in a table. However I just now learned that apparently despite using remove the other objects don’t shift up. The object I deleted is still considered there but as nil.

Is there anyway I could make a table where I can safely delete objects where the other objects will shift correctly?

Visual

{A, B, C, D} 
-- Remove B
{A, nil, C, D}
-- What I want:
{A, C, D}

PS: In case I’m mistaken and it actually is supposed to be doing what I am after, then let me know, it’s probably something I’m doing.

It’s an issue on my end.

My Code:

Summary
function level:ExtendRooms(level)
	local max = script.Parent.Settings.Rooms.Value
	while #level.Rooms ~= max and #level.Doors ~= 0 do --wait() 
		--RunService.Heartbeat:Wait() 
		local roomsFound = Components:GetRooms(level.Type):GetChildren()
		local roomOptions = {unpack(roomsFound)}
		wait(0.1)
		local randomDoor = GetRandomDoor(level)
		if(randomDoor) then 
			local CompletedExtension = false
			for i,v in pairs(roomOptions) do
				print("LOOP ON", #roomOptions)
				local overlapping = Utility:CheckOverlap(newRoom.CollisionBox)
				if not overlapping then
					CompletedExtension = true
					table.insert(Utility.CollisionBoxes, newRoom.CollisionBox)
					table.insert(level.Rooms, newRoom)
					break
				else
					for j, g in pairs(roomOptions) do
						if g == v then
							table.remove(roomOptions, j)
							print(#roomOptions)
						end
					end
					newRoom:Destroy()
				end
			end
		end
	end
end

What I’m doing:
I’m trying to make the dungeon stop re-using already tested rooms that failed collision. What I do is copy over the list of possible rooms, and remove the ones that failed the test.

For some reason, I manage to get nil on a few, so it never manages to go to LOOP ON 2, 1, etc.

For some reason, nil starts appearing for the objects around that point.

FULL LIST BEFORE REMOVAL:
image

I’m trying to avoid this:
image
Instead of letting it fail some 20+ times, Im trying to make each new segment only be about 6, because there are 7 rooms.

2 Likes

Yeah table.remove should be doing this. I have used it many times without any issues. I would be curious to see your actual code

3 Likes

I posted my code and wrote an explanation of what I’m trying to achieve.

So I found out… it’s breaking back to the start of the “while” loop after overlapping three times, all the time. No idea why…

image

One Issue I can see is that your loops wouldn’t cover all of the elements in roomOptions, since you’re using table.remove while working down the array. You’ll want to step backwards through it so that all the shifting happens on elements you’ve already tested. Edit: Or step forward and do some other check to stop.

Not sure if that’s relevant to the problem, since you loop again with while.

local tab = {1, 2, 3, 4, 5}

-- This outputs (1, 1), (2, 3), (3, 5)
for i, v in pairs(tab) do
	print(i, v)
	table.remove(tab, i)
end
1 Like

Removing elements from the array part of the table while iterating over it is certainly the problem. You can’t even do this safely with ipairs. The more bullet-proof approaches would be to mark the objects you’re going to delete and remove them after the iteration, or for the loop to copy the “keepers” into a new temporary array as it iterates, which is then assigned back to roomOptions after the loop.

Also, did the inner for loop even need to exist? It appears to be doing a linear search for
the index of room v, but that’s already known and stored in i, unless v is a non-unique identifier rather than an object/table and the same value is expected to be in the array more than once, but the example suggests otherwise.

5 Likes

The first loop while is a section meant to find a random room, and get a random door. I deleted that part out because it wasn’t related to the loop itself, just a bunch of gets.

The for loop is the segment where I attempt to “OK” (check for overlaps with region3) the placement of a randomly picked room to attach to the randomly picked door of a room that’s already placed down.

It supposed to break off from the FOR loop once it’s found a room, and go back into the while loop.

I meant specifically this for loop inside the other for loop:

for j, g in pairs(roomOptions) do
    if g == v then
        table.remove(roomOptions, j)
        print(#roomOptions)
    end
end