Trying to loop through a dictionary and destroy values which are too far from the player, but for some reason it sometimes randomly errors with
invalid key to ‘next’
Looked around and seems like this error is caused because I’m removing values from the dictionary at the same time I’m looping through it which causes it to get confused.
for position, chunk in pairs(Chunks) do
local Distance = (chunk.PositionValue.Value - Vector3.new(Camera.CFrame.X,0,Camera.CFrame.Z)).magnitude
if Distance > 700 then
chunk:Destroy()
Chunks[position] = nil
wait()
end
end
This happens because you are modifying the table while iterating over it. You could build a list of Chunks to remove while iterating over the table and then remove them all at the end.
Alternatively, if you change this to an Array rather than a dictionary you can iterate over it in reverse order and safely modify it while iterating over it.
Extending on TheGamer101’s reply, here’s an example of how we’d go about doing this:
local fooValues = {
foo1 = "bar",
foo2 = "bar",
foo3 = "baz",
foo4 = "bar",
foo5 = "baz",
}
local removedFoos = {}
-- add all keys with value of "baz" to removedFoos.
for foo, value in pairs(fooValues) do
if value == "baz" then
table.insert(removedFoos, foo)
end
end
-- now that we're finished iterating over the table, we can remove its elements.
for _, removedFoo in ipairs(removedFoos) do
fooValues[removedFoo] = nil
end
-- check if the elements were removed.
for foo, value in pairs(fooValues) do
print(foo, value) --> foo2 bar foo1 bar foo4 bar
end
The official Lua manual explicitly mentions that you should be fine doing that (see below). It’s possible you using pairs is causing unforseen issues, or that Roblox has otherwise modified next/pairs though. Either way, try using next, Chunks instead of pairs(Chunks) to see if that fixes the issue.
From the Lua manual
next (table [, index])
Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. next returns the next index of the table and its associated value. When called with nil as its second argument, next returns an initial index and its associated value. When called with the last index, or with nil in an empty table, next returns nil . If the second argument is absent, then it is interpreted as nil . In particular, you can use next(t) to check whether a table is empty.
The order in which the indices are enumerated is not specified, even for numeric indices . (To traverse a table in numeric order, use a numerical for or the ipairs.)
The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table. You may however modify existing fields. In particular, you may clear existing fields.
It’s completely safe to remove (or change) entries from a table as you iterate over it. What’s not safe is adding new entries.
Do you happen to add new entries to the Chunks table somewhere else while this loop is yielding (during the wait)? That invalidates iterator states of iterators on that table (due to reallocations etc.) and can cause that error (or other odd behavior, such as iterating elements multiple times).
The Lua spec states that you can change existing keys in a table while iterating through it (especially to nil.)
It’s happening because you are yielding (“wait”-ing) while iterating over a table. You likely have another script that adds new keys to the table during this yield.
You should add a “break” after the “wait()”, then start the loop over, like this:
repeat
local changed = false
local CameraCFrame = Camera.CFrame
local CameraPosition = Vector3.new(CameraCFrame.X, 0, CameraCFrame.Z)
for position, chunk in pairs(Chunks) do
local Distance = (chunk.PositionValue.Value - CameraPosition).magnitude
if Distance > 700 then
chunk:Destroy()
Chunks[position] = nil
wait()
changed = true
break
end
end
until not changed
It may check the same chunks more than initially intended, but it should work.