Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

cm0s

macrumors newbie
Original poster
Feb 12, 2009
2
0
Hello,

files is an array of File objects.
I want to enumerate through the file array and check for File objects that has equal md5 digest.
I want to remove the file objects from the array that are copies of currentFile, but I can't remove objects from the files array while it's enumerating. Can I achieve this by some other means?

Code:
for (File *currentFile in files) {
	NSPredicate *predicate = [NSPredicate predicateWithFormat: @"md5Hash == %@ && filePath != %@", [currentFile md5Hash], [currentFile filePath]]; // This will check for files that has equal md5 digest but not the same filepath (ie true if it's a file duplicate)
		
	if ([[files filteredArrayUsingPredicate:predicate] count] > 0) // If copies are found
	{
		[currentFile addCopies:[files filteredArrayUsingPredicate:predicate]]; // The file class has an array which stores its copies
		[files removeObjectsInArray:[files filteredArrayUsingPredicate:predicate]]; // Can't do this. :(
	}	
}
 

HiRez

macrumors 603
Jan 6, 2004
6,265
2,630
Western US
Instead of using an enumerator, just use a simple while loop with an integer index, which you will use to access the array elements using objectAtIndex:. Increment the index each time through the loop, unless you remove an element, in which case you will not increment the index since the same index will now point to the object after the one you just deleted (it'll move up a spot in the array). You just need to make sure that your loop termination condition, where you check to see if you're at the last index, gets updated with the new size of the array each time you remove an object (or get the array size dynamically each time through the loop). You could also start at the last index and decrement the index, making sure you don't go below zero and checking for nil in case you removed every object in the array.
 

MacRumors Guy

macrumors member
Sep 17, 2008
82
0
From the NSEnumerator documentation:

Note: It is not safe to modify a mutable collection while enumerating through it. Some enumerators may currently allow enumeration of a collection that is modified, but this behavior is not guaranteed to be supported in the future.

Using the removeObjectAtIndex method you have no performance guarantees.
 

Krevnik

macrumors 601
Sep 8, 2003
4,101
1,312
The problem here is that mutating an array will give you grief, so you need to move the mutate outside the loop. To do that, you need to keep track of what files need to be removed in a separate collection.

Code:
NSMutableArray *filesToRemove = [NSMutableArray array]; // A set would be nicer, but then File needs to have a good hash method.

for (File *currentFile in files) {
        if([filesToRemove containsObject:currentFile]) // Skip Dupes
                continue;

	NSPredicate *predicate = [NSPredicate predicateWithFormat: @"md5Hash == %@ && filePath != %@", [currentFile md5Hash], [currentFile filePath]]; // This will check for files that has equal md5 digest but not the same filepath (ie true if it's a file duplicate)

        NSArray *filteredArray = [files filteredArrayUsingPredicate:predicate];
		
	if ([filteredArray count] > 0) // If copies are found
	{
		[currentFile addCopies:filteredArray];
                [filesToRemove addObjectsFromArray:filteredArray];
	}	
}

[files removeObjectsInArray:filesToRemove];

The code behavior should be identical to the code you had before, but will now work (I think).
 

kpua

macrumors 6502
Jul 25, 2006
294
0
Krevnik's approach is good, but you can also use an NSIndexSet and removeObjectsAtIndexes:. NSIndexSet is a lot lighter weight than another array and it will be more efficient when removing because NSMutableArray won't have to do an -indexOfObject:
 

cm0s

macrumors newbie
Original poster
Feb 12, 2009
2
0
Thank you very much for the replies.

Krevniks code worked very well, greatly appreciated. :)

I'll read up on NSIndexSet and see how I can implement it to improve performance.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.