Delete items from a list
From REALbasicWiki
| Overall article skill | ✭ |
Say you have a list of items, be it a true array or an indexed list like FolderItem.TrueItem(idx) or ListBox.List(idx), and you need to scan the list to remove some items from it. You could be tempted to write:
Sub RemoveSomeItems( fromArray() as String )
for idx as integer = 0 to fromArray.Ubound
if ((the condition to remove the item )) then
fromArray.Remove idx
end if
next
End Sub
... and it does not work !!.
Contents |
[edit] Why does not it work ?
The problem is that if you remove an item from your array, following items will be moved. Have a look at the following:
We are in a For...Next loop with i as the counter. If you delete Item 2, the following items will be moved but Next has not been executed yet. On execution, it normally increments the counter so i = 3. At this index, you wanted to find Item 3 but you actually get Item 4.
As a summary, you will miss the immediately following item each time you Delete an item.
[edit] Better design proposals
There are many ways to achieve this task properly. Here are some suggestions.
[edit] Adjust the index depending on the removal of an item
If an item gets deleted from an array, the items after it move down index the deletion position. With this understanding, we can keep the index unchanged when an item gets removed from the array:
dim idx as Integer = 0
while idx <= ubound( fromArray ) //Stop at the end of the array
if ((the condition to delete the item )) then
fromArray.Remove idx // If we delete an item, we DO NOT increment the index
else
idx = idx + 1 // Increment the index as we did not remove the item
end if
wend
[edit] Use an array that references the items for deletion
When it comes to deleting files (remember that they are immediately deleted and not put into the trash), which can be referenced by FolderItem objects, one can collect those references, which won't get altered while the items get deleted:
Sub DeleteSomeFiles( fromFolder as FolderItem )
Dim f as FolderItem
Dim filesToDelete() as FolderItem
//We scan the folder and postpone deletion
for idx as integer = 1 to fromFolder.Count
f = fromFolder.TrueItem( idx )
if f <> nil and ((the condition to delete the file )) then
filesToDelete.Append f
end if
next
//Here, you even have the opportunity to ask confirmation from the end-user
MsgBox "The following files will be deleted..."
//...
//Then we actually delete the files
for each f in filesToDelete
f.Delete
next
End Sub
The big difference here is that we store the FolderItem reference in the filesToDelete() array, so we do not even need an explicit counter to delete the file. Storing the index of the files in the array would be a mistake as we would run into the same problems as described above.
- See Delete a non-empty folder for a more advanced version of a DeleteFolder function.
[edit] Start from the end
In some cases, you also can just run the loop with the index going backwards:
for idx as integer = ubound( myArray ) downto 0
if ((the condition to delete the item )) then
myArray.Remove idx
end if
Next
[edit] Create a new list
Another way to approach this task is to create a new list. This offers the advantages of faster execution, simpler logic, and easier testability.
dim newList() as String
for i as Integer = 0 to UBound(oldList)
if not ((the condition to delete the item )) then
newList.Append oldList(i)
end if
next

