NSMutableArray cannot remove duplicates - objective-c

I have duplicates in my array and i want to get rid of them, so i run this loop, however it doesn't work. Any one know why?
The array currently has 3 items, 2 duplicates and 1 unique.
for (int x = 0; x <= [array count]; x++) {
if(x > 0){
if([[array objectAtIndex:x - 1] isEqualToString:[array objectAtIndex:x]]){
[array removeObjectAtIndex:x];
}
}
}

You can't iterate over an object and modify it at the same time. Once you remove an object, the indexes of all the objects change. You can try copying the array first and iterate that and make the modifications in the original array, but you still might have to change some of your logic depending on what you're trying to accomplish.

Your algorithm only ever compares items that are next to each other in the array (the items at positions x and x-1). If the duplicates are in any other positions, they won't be found.
The naïve way to fix this is to do a double loop. Compare each item in the array with every item after it. This will start taking an extremely long time as your array becomes bigger.
The correct way to do this is to let the framework handle the operation. Convert your array to a set (which does not have duplicates by definition) and then back to an array:
NSSet * s = [NSSet setWithArray:array];
NSArray * dedupedArray = [s allObjects];
If you need to preserve the order, you'll have to do this in a slightly roundabout way, although this is still faster than the double-loop:
NSMutableSet * itemsSeen = [NSMutableSet set];
NSMutableArray * dedupedArray = [NSMutableArray array];
for( id item in array ){
if( ![itemsSeen containsObject:item] ){
[itemsSeen addObject:item];
[dedupedArray addObject:item];
}
}

I would suggest simply using NSSet ( or NSMutableSet ). It will automatically ensure you have only one of every object.
BUT - notice it is one of every OBJECT. It can have 2 objects that are different but have the same inner value.

If you want to ensure that there are no duplicates in your array, it would be better to use an NSMutableSet rather than an NSMutableArray.
NSMutableSet maintains the invariant that every object in the set is unique.
For example:
NSMutableSet* set = [NSMutableSet set];
NSString* data = #"Data";
[set addObject:data];
[set addObject:data];
The second call to addObject: will do nothing as data is already in the set.

Related

how to remove objects from NSMutableArray correctly?

I try to delete items from NSMutableArray in loop. I have an array :(2,3,4,5,6).
int j = [array count];
while (array != NULL) {
NSUInteger g = 0;
for (int q = 0; q < j; q++) {
[array removeObjectAtIndex:g];
}
When i set a breakpoint (after first iteration) i got the following:
[0]=(id)0x00000000
[1]=(id)0x071421a0(int)3
[2]=(id)0x071421b0(int)4
[3]=(id)0x071421e0(int)5
I don't understand how to delete in each iteration a first object. I mean that following it will be first. And why the last one is disappeared every time also?
Thanks.
Another way to delete a collection of objects from an array is to add the objects you want to delete to a separate array, then use that to delete your objects from the primary array in one fell swoop. There are benefits to this, least of which would be no risk of out-of-bounds, and also the possibility for rolling back since you are essentially removing in a single batch process:
int j = [array count];
NSMutableArray *theseObjects = [NSMutableArray array];
for (int q = 0; q < j; q++)
{
id thisObject = [array objectAtIndex:q];
BOOL shouldRemoveThisObject = ...//<--determine if you want to remove this object
if (shouldRemoveThisObject)
[theseObjects addObject:thisObject];
}
[array removeObjectsInArray:theseObjects];
It looks like you want to remove all entries. Then you could also call [myArray removeAllObjects];
You cant do as you are trying to do.
j is set to 5 (array count) but the array's size is reducing on every iteration. So at one point of time it will try to access array out-of-bound, hence exception and your app will crash.
Instead you need to do as below:
while (array.count > 0) {
NSLog(#"Arr before removal is : %#",array);
[array removeObjectAtIndex:0];
NSLog(#"Arr after removal is : %#",array);
}
If for some other reason you want to remove only first then the above will work.
If you want to remove all the objects at one go: you can use:
[array removeAllObjects];
You CAN'T remove objects from NSMutableArray inside a loop. This is because, in that case, array size is changing over each iteration (worst case) and you could try to access to an out of bounds index.
The best option is Jeremy's solution. Keep desired objects on separated array and then remove these objects from the main array, outside of the loop.

Check duplicate property values of objects in NSArray

I have an NSArray containing objects with a size property.
How can I check if the NSArray has two objects with the same value for size?
Can I do something like:
int i = 0;
for (id item1 in myArray) {
NSDecimalNumber *size1 = [item1 size];
for (id item2 in myArray) {
NSDecimalNumber *size2 = [item2 size];
if ([size1 isEqual:size2]) {
i ++;
}
}
}
if (i > [myArray count]) {
NSLog(#"Duplicate Sizes Exist");
}
Or is there an easier way?
Try this code:
NSSet *myset = [NSSet setWithArray:[myarray valueForKey:#"size"]];
int duplicatesCount = [myarray count] - [myset count];
size here is the object property.
Use NSCountedSet. then add all your objects to the counted set, and use the countForObject: method to find out how often each object appears in your array.
You can check this link also how-to-find-duplicate-values-in-arrays
Hope it helps you
Probably simplest is to sort the array based on the size field and then step through the sorted list looking for adjacent dupes.
You could also "wrap" each object in one that exports the size as its key and use a set. But that's a lot of extra allocations.
But if you only want to know if dupes exist, and not which ones they are, create an NSNumber for each object's size and insert the NSNumbers in a set. The final size will tell you how many dupes.
NSArray *cleanedArray = [[NSSet setWithArray:yourArraywithDuplicatesObjects ] allObjects];
Use Sets this will remove all duplicates objects.Will return NSArrayNSCountedSet and use countForObject: method to find out how often each object appears how many times.

How does one retrieve a random object from an NSSet instance?

I can grab a random value from an array-like structure by retrieving a random index.
How can I grab a random value from an NSSet object that stores NSNumber objects? I couldn't find an instance method of NSSet that retrieves a random value.
In short, you can't directly retrieve a random object from an NSSet.
You either need to turn the set into an array -- into something that has an index that can be randomized -- by re-architecting your code to use an array or you could implement this using this bit of pseudo-code:
randomIndex = ...random-generator....(0 .. [set count]);
__block currentIndex = 0;
__block selectedObj = nil;
[set enumerateObjectsWithOptions:^(id obj, BOOL *stop) {
if (randomIndex == currentIndex) { selectedObj = obj; *stop = YES }
else currentIndex++;
}];
return selectedObj;
Yes -- it iterates the set, potentially the whole set, when grabbing the object. However, that iteration is pretty much what'll happen in the conversion to an NSArray anyway. As long as the set isn't that big and you aren't calling it that often, no big deal.
Whilst I like that #bbum answer will terminate early on some occasions due to the use of stop in the enumeration block.
For readability and ease of remembering what is going on when you revisit this code in the future I would go with his first suggestion of turn the set into an array
NSInteger randomIndex = ..random-generator....(0 .. [set count])
id obj = [set count] > 0 ? [[set allObjects] objectAtIndex:randomIndex] : nil;

iOS indexOfObjectIdenticalTo: issue

I have two arrays, each containing strings. The first array is a list of words, the second array contains alternatives to those words in different languages.
The arrays are matched such that the word at index n in the second array is a translation of the word at index n in the first array.
The words and their translations are displayed in a table view. The user can filter the table view by entering text in a search field. When this is done, I create a filtered array from the first array like this:
- (void)filterContentForSearchText:(NSString*)searchText
[self.filteredarray removeAllObjects];
[firstarray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj compare:searchText options:NSCaseInsensitiveSearch range:NSMakeRange(0, [searchText length])] == NSOrderedSame)
{
idx= [firstarray indexOfObjectIdenticalTo:obj];
NSUInteger maxindex = idx + 50;
for (idx ; (idx < [firstarray count] && idx <= maxindex && idx!= NSNotFound); idx ++)
{
[self.filteredarray addObject:[firstarray objectAtIndex: idx]];
}
*stop = YES;
}
}];
Then, when I am displaying the values in my table view, I use the following code. This is an exerpt from my cellForRowAtIndexPath method. I am trying to get the index from the original array using the object that has been added to the filtered array.
contentForThisRow = [self.filteredarray objectAtIndex:row];
NSUInteger index = [self.firstarray indexOfObjectIdenticalTo:contentForThisRow];
contentForThisRow2 = [self.secondarray objectAtIndex:index];
This works on the simulator, but on the device I will sometimes get repeats of the same entry from the second array. For example, my first array contains the word "hello" three consecutive times, at indexes x, y and z. My second array contains "hei", "heisan" and "hoppsan", which are all translations of "hello", at indexes x, y and z.
On the simulator, I get three cells, each with a different translation. On the device, I get three cells, all with "hei", the first translation. This does not happen for all repeated translations.
Why is this happening, and how can I get around it?
I think the problem is that iOS (on the device) may be using a slightly different optimisation to the emulator somewhere, either in NSString or NSArray. That is a guess.
indexOfObjectIdenticalTo: returns the index of the first object that has the same memory address as the object you are passing in. On the phone it appears to have re-used the identical string objects in your first array when building the filtered array (possibly even when building firstArray), so you are getting the same index value back each time.
A better solution would be to build your filtered array as an array of dictionaries, storing the values from the correct indexes of firstArray and secondArray at that point. You can then use these values directly when populating the cell instead of searching through both arrays again. This should also have some performance benefits.
You would achieve this using the following code. First, inside your loop when you are building the filtered array, instead of adding the object from firstarray, do this:
[self.filteredArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:[firstarray objectAtIndex:idx],#"english",[secondarray objectAtIndex:idx],#"translated",nil];
Then, in your cellForRowAtIndexPath, to get your two content variables:
NSDictionary *rowData = [self.filteredarray objectAtIndex:row];
contentForThisRow = [rowData objectForKey:#"english"];
contentForThisRow2 = [rowData objectForKey:#"translated"];
An even better solution would be to hold your data like this in the first place, and not try to keep two separate arrays synchronised. I imagine if you want to add or alter anything in your two separate files you could quickly get them out of step. However, I feel I've done enough for the day...
else
contentForThisRow = [self.firstarray objectAtIndex:row];
contentForThisRow2 = [self.secondarray objectAtIndex:row];
You see anything wrong with that?

Best way to remove from NSMutableArray while iterating?

In Cocoa, if I want to loop through an NSMutableArray and remove multiple objects that fit a certain criteria, what's the best way to do this without restarting the loop each time I remove an object?
Thanks,
Edit: Just to clarify - I was looking for the best way, e.g. something more elegant than manually updating the index I'm at. For example in C++ I can do;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
For clarity I like to make an initial loop where I collect the items to delete. Then I delete them. Here's a sample using Objective-C 2.0 syntax:
NSMutableArray *discardedItems = [NSMutableArray array];
for (SomeObjectClass *item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addObject:item];
}
[originalArrayOfItems removeObjectsInArray:discardedItems];
Then there is no question about whether indices are being updated correctly, or other little bookkeeping details.
Edited to add:
It's been noted in other answers that the inverse formulation should be faster. i.e. If you iterate through the array and compose a new array of objects to keep, instead of objects to discard. That may be true (although what about the memory and processing cost of allocating a new array, and discarding the old one?) but even if it's faster it may not be as big a deal as it would be for a naive implementation, because NSArrays do not behave like "normal" arrays. They talk the talk but they walk a different walk. See a good analysis here:
The inverse formulation may be faster, but I've never needed to care whether it is, because the above formulation has always been fast enough for my needs.
For me the take-home message is to use whatever formulation is clearest to you. Optimize only if necessary. I personally find the above formulation clearest, which is why I use it. But if the inverse formulation is clearer to you, go for it.
One more variation. So you get readability and good performace:
NSMutableIndexSet *discardedItems = [NSMutableIndexSet indexSet];
SomeObjectClass *item;
NSUInteger index = 0;
for (item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addIndex:index];
index++;
}
[originalArrayOfItems removeObjectsAtIndexes:discardedItems];
This is a very simple problem. You just iterate backwards:
for (NSInteger i = array.count - 1; i >= 0; i--) {
ElementType* element = array[i];
if ([element shouldBeRemoved]) {
[array removeObjectAtIndex:i];
}
}
This is a very common pattern.
Some of the other answers would have poor performance on very large arrays, because methods like removeObject: and removeObjectsInArray: involve doing a linear search of the receiver, which is a waste because you already know where the object is. Also, any call to removeObjectAtIndex: will have to copy values from the index to the end of the array up by one slot at a time.
More efficient would be the following:
NSMutableArray *array = ...
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
if (! shouldRemove(object)) {
[itemsToKeep addObject:object];
}
}
[array setArray:itemsToKeep];
Because we set the capacity of itemsToKeep, we don't waste any time copying values during a resize. We don't modify the array in place, so we are free to use Fast Enumeration. Using setArray: to replace the contents of array with itemsToKeep will be efficient. Depending on your code, you could even replace the last line with:
[array release];
array = [itemsToKeep retain];
So there isn't even a need to copy values, only swap a pointer.
You can use NSpredicate to remove items from your mutable array. This requires no for loops.
For example if you have an NSMutableArray of names, you can create a predicate like this one:
NSPredicate *caseInsensitiveBNames =
[NSPredicate predicateWithFormat:#"SELF beginswith[c] 'b'"];
The following line will leave you with an array that contains only names starting with b.
[namesArray filterUsingPredicate:caseInsensitiveBNames];
If you have trouble creating the predicates you need, use this apple developer link.
I did a performance test using 4 different methods. Each test iterated through all elements in a 100,000 element array, and removed every 5th item. The results did not vary much with/ without optimization. These were done on an iPad 4:
(1) removeObjectAtIndex: -- 271 ms
(2) removeObjectsAtIndexes: -- 1010 ms (because building the index set takes ~700 ms; otherwise this is basically the same as calling removeObjectAtIndex: for each item)
(3) removeObjects: -- 326 ms
(4) make a new array with objects passing the test -- 17 ms
So, creating a new array is by far the fastest. The other methods are all comparable, except that using removeObjectsAtIndexes: will be worse with more items to remove, because of the time needed to build the index set.
Either use loop counting down over indices:
for (NSInteger i = array.count - 1; i >= 0; --i) {
or make a copy with the objects you want to keep.
In particular, do not use a for (id object in array) loop or NSEnumerator.
For iOS 4+ or OS X 10.6+, Apple added passingTest series of APIs in NSMutableArray, like – indexesOfObjectsPassingTest:. A solution with such API would be:
NSIndexSet *indexesToBeRemoved = [someList indexesOfObjectsPassingTest:
^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [self shouldRemove:obj];
}];
[someList removeObjectsAtIndexes:indexesToBeRemoved];
Nowadays you can use reversed block-based enumeration. A simple example code:
NSMutableArray *array = [#[#{#"name": #"a", #"shouldDelete": #(YES)},
#{#"name": #"b", #"shouldDelete": #(NO)},
#{#"name": #"c", #"shouldDelete": #(YES)},
#{#"name": #"d", #"shouldDelete": #(NO)}] mutableCopy];
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if([obj[#"shouldDelete"] boolValue])
[array removeObjectAtIndex:idx];
}];
Result:
(
{
name = b;
shouldDelete = 0;
},
{
name = d;
shouldDelete = 0;
}
)
another option with just one line of code:
[array filterUsingPredicate:[NSPredicate predicateWithFormat:#"shouldDelete == NO"]];
In a more declarative way, depending on the criteria matching the items to remove you could use:
[theArray filterUsingPredicate:aPredicate]
#Nathan should be very efficient
Here's the easy and clean way. I like to duplicate my array right in the fast enumeration call:
for (LineItem *item in [NSArray arrayWithArray:self.lineItems])
{
if ([item.toBeRemoved boolValue] == YES)
{
[self.lineItems removeObject:item];
}
}
This way you enumerate through a copy of the array being deleted from, both holding the same objects. An NSArray holds object pointers only so this is totally fine memory/performance wise.
Add the objects you want to remove to a second array and, after the loop, use -removeObjectsInArray:.
this should do it:
NSMutableArray* myArray = ....;
int i;
for(i=0; i<[myArray count]; i++) {
id element = [myArray objectAtIndex:i];
if(element == ...) {
[myArray removeObjectAtIndex:i];
i--;
}
}
hope this helps...
Why don't you add the objects to be removed to another NSMutableArray. When you are finished iterating, you can remove the objects that you have collected.
How about swapping the elements you want to delete with the 'n'th element, 'n-1'th element and so on?
When you're done you resize the array to 'previous size - number of swaps'
If all objects in your array are unique or you want to remove all occurrences of an object when found, you could fast enumerate on an array copy and use [NSMutableArray removeObject:] to remove the object from the original.
NSMutableArray *myArray;
NSArray *myArrayCopy = [NSArray arrayWithArray:myArray];
for (NSObject *anObject in myArrayCopy) {
if (shouldRemove(anObject)) {
[myArray removeObject:anObject];
}
}
benzado's anwser above is what you should do for preformace. In one of my applications removeObjectsInArray took a running time of 1 minute, just adding to a new array took .023 seconds.
I define a category that lets me filter using a block, like this:
#implementation NSMutableArray (Filtering)
- (void)filterUsingTest:(BOOL (^)(id obj, NSUInteger idx))predicate {
NSMutableIndexSet *indexesFailingTest = [[NSMutableIndexSet alloc] init];
NSUInteger index = 0;
for (id object in self) {
if (!predicate(object, index)) {
[indexesFailingTest addIndex:index];
}
++index;
}
[self removeObjectsAtIndexes:indexesFailingTest];
[indexesFailingTest release];
}
#end
which can then be used like this:
[myMutableArray filterUsingTest:^BOOL(id obj, NSUInteger idx) {
return [self doIWantToKeepThisObject:obj atIndex:idx];
}];
A nicer implementation could be to use the category method below on NSMutableArray.
#implementation NSMutableArray(BMCommons)
- (void)removeObjectsWithPredicate:(BOOL (^)(id obj))predicate {
if (predicate != nil) {
NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:self.count];
for (id obj in self) {
BOOL shouldRemove = predicate(obj);
if (!shouldRemove) {
[newArray addObject:obj];
}
}
[self setArray:newArray];
}
}
#end
The predicate block can be implemented to do processing on each object in the array. If the predicate returns true the object is removed.
An example for a date array to remove all dates that lie in the past:
NSMutableArray *dates = ...;
[dates removeObjectsWithPredicate:^BOOL(id obj) {
NSDate *date = (NSDate *)obj;
return [date timeIntervalSinceNow] < 0;
}];
Iterating backwards-ly was my favourite for years , but for a long time I never encountered the case where the 'deepest' ( highest count) object was removed first. Momentarily before the pointer moves on to the next index there ain't anything and it crashes.
Benzado's way is the closest to what i do now but I never realised there would be the stack reshuffle after every remove.
under Xcode 6 this works
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array)
{
if ( [object isNotEqualTo:#"whatever"]) {
[itemsToKeep addObject:object ];
}
}
array = nil;
array = [[NSMutableArray alloc]initWithArray:itemsToKeep];