PDA

View Full Version : How does ipairs handle deletes from a table?


Niwashi
05-20-2012, 09:23 PM
I want to have something like the following in my script:

-- remove items from currencyList that are no longer in the wallet
for i, currencyItem in ipairs(currencyList) do

-- check if this is still in the wallet
local walletItem = GetWalletItemByName(currencyItem.Name);

if walletItem == nil then -- item no longer in wallet, so remove from list

table.remove(currencyList, i);
ChangesMade = true;

end

end

but I'm not sure how the table.remove will impact cycling through items in the table. In C++, I'd deal with that by iterating backwards through the table, from the last item to the first one, so that removals don't move the items I still have to search through. In LUA, I could iterate backwards with a for loop using the pattern "for i = 10, 1, -1 do", but using the form "for k, v in ipairs(t) do" there doesn't seem to be a step parameter to specify that I want to search backwards. Will the code above hit every item in the table, even if the indexes change from a call to table.remove? Or if not, then how do I search through a table to find items that need to be removed?

(Sorry if this is an elementary question, but I'm still learning the LUA scripting language, and the online tutorial on it that I found didn't cover this question.)

D.H1cks
05-21-2012, 05:29 AM
An interesting question, and now I know the answer myself. I suggest that whenever you have questions about how LUA operates, set up an experiment and you can discover things. For example, for your problem I wrote the following in a dummy plugin:

self.testTable = { "apple", "oranges", "cherries", "bananas", "tomato", "strawberries", "blueberries", "pears", "blackberries" }

for i, item in ipairs(self.testTable) do
Turbine.Shell.WriteLine(i .. ": " .. item);
end

Turbine.Shell.WriteLine("Delete Item");

for i, item in ipairs(self.testTable) do
if(item == "tomato" or item == "strawberries" ) then
table.remove(self.testTable, i);
end
end

Turbine.Shell.WriteLine("Next loop");

for i, item in ipairs(self.testTable) do
Turbine.Shell.WriteLine(i .. ": " .. item);
end

The code basically will attemp to remove 2 items from the table that are next to each other, here was the output:

### Chat Log: General 05/21 06:06 AM ###
1: apple
2: oranges
3: cherries
4: bananas
5: tomato
6: strawberries
7: blueberries
8: pears
9: blackberries
Delete Item
Next loop
1: apple
2: oranges
3: cherries
4: bananas
5: strawberries
6: blueberries
7: pears
8: blackberries
Loaded plugin "tester".

As you can see, the second item did not get deleted.

Also, I tried to remove both "tomato" and "blueberries", which worked exactly as expected. So basically, removing a single item from a table while iterating with ipairs means you cannot access the item immediately following the item you delete.

I think your best bet is to do your reverse iteration:

for i = table.getn(tablename), 1, -1 do
local item = tablename[i];

-- search and remove code
end