Iam trying to remove elements in a table which have a certain value
test = {
{
id = 473385593,
deleted = true,
},
{
id = 473385619,
deleted = true,
},
{
id = 473432275,
deleted = false,
},
{
id = 473786710,
deleted = false,
},
}
for index, value in ipairs(test) do
if value.deleted then
table.remove(test, index)
end
end
for _, value in pairs(test) do
print(value.id)
print(value.deleted)
end
I would like that i the output is
473432275
false
473786710
false
But
{
id = 473385619,
deleted = true,
},
is not deleted
So i guess as im iterating over the table to access the value and then do something when deleted == true i remove that element but also reorder the table and so i skip the next element after i call table.remove. At least thats what i figured whats happening.
So how do i remove in lua a tables elements based on their value?
As you figured out already, this snippet
for index, value in ipairs(test) do
if value.deleted then
table.remove(test, index)
end
end
is doing the grave mistake of removing table elements - shifting subsequent elements down - while iterating a table. for index, value in ipairs(test) do <body> end
is roughly equivalent to
do
local index = 0
while true do
index = index + 1
local value = test[index]
if value == nil then
break
end
<body>
end
end
So of course if <body>
does a table.remove(test, index)
, then in the next iteration test[index]
will be the previous test[index+1]
and you will skip an element.
I would suggest "filtering" test, and building a new table containing only the elements you want to keep. This would look as follows:
local test_filtered = {}
for index, value in ipairs(test) do
if not value.deleted then
table.insert(test_filtered, value)
end
end
for _, value in ipairs(test_filtered) do
print(value.id)
print(value.deleted)
end
This is asymptotically optimal runtime-wise (linear time). Very often it's cleaner to not mutate the original table; that can lead to surprises.
In case you really want to modify test
, you could iterate in reverse order, e.g. using for i = #test, 1, -1 do
. Then table.remove
will work fine. But be warned that this has a time complexity bound of O(n²).
By keeping a second index you can also do in-place bulk removal of elements in O(n).
Also: Why do you use a "list" here at all? Assuming that you don't care about order, a "hash table" (which lets you remove elements while iterating) would make more sense, given that you have IDs. Consider
test = {
[473385593] = {deleted = true},
[473385619] = {deleted = true},
[473432275] = {deleted = false},
[473786710] = {deleted = false},
}
then you could just do
for id, value in pairs(test) do
if value.deleted then
test[id] = nil
end
end