2

I've read every similar question, but have determined either I'm doing something stupid (possible) or I fail to grasp the NSArray method containsObject:

I'm trying to setup a UITableView that contains saved "favorites"; locations that are kept as a custom class called "MapAnnotations." This contains stuff like coordinates, title, an info field, and a couple of other parameters. I'm successfully saving/retrieving it from a NSUserDefaults instance, but can't seem to successfully detect duplicate objects held in my NSMutableArray.

Here's the relevant code:

-(void)doSetUp
{
//load up saved locations, if it exists

NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];

//if there are saved locations
if ([myDefaults objectForKey:@"savedLocations"]) {

    NSLog(@"file exists!");

      //get saved data and put in a temporary array
    NSData *theData = [myDefaults dataForKey:@"savedLocations"];
      //my custom object uses NSCode protocol
    NSArray *temp = (NSArray *)[NSKeyedUnarchiver unarchiveObjectWithData:theData];
    NSLog(@"temp contains:%@",temp);
      //_myFavs currently exists as a NSMutableArray property
    _myFavs = [temp mutableCopy];

}else{

    NSLog(@"File doesn't exist");
    _myFavs = [[NSMutableArray alloc]init];
}

    //_currLoc is an instance of my Mapnnotations custom class
        // which contains coordinates, title, info, etc.

if (_currLoc != nil) {

        //if this is a duplicate of a saved location

    if ([_myFavs containsObject:_currLoc]) {

        //pop an alert

        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Sorry..." message:@"That location has already been saved." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
    }else{
            //add location to end of myFavs array
        [_myFavs addObject:_currLoc];

        NSLog(@"myFavs now contains:%@",_myFavs);

        //write defaults

        NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:_myFavs];
        [myDefaults setObject:encodedObject forKey:@"savedLocations"];
        [myDefaults synchronize];
        }
    }
}

I've tried enumerating through the _myFavs array, checking for matches on specific fields (get errors for enumerating through something mutable), tried to copy to a straight array... tried to use indexOfObject:..

2
  • You are trying to detect duplicates in order to remove them, right? Commented Mar 4, 2014 at 18:33
  • Yes, or actually, to bail before adding them. Commented Mar 4, 2014 at 18:35

2 Answers 2

5

You can use containsObject: method with custom objects that implement isEqual: method. Adding an implementation of this method to your Mapnnotations class will fix the problem:

// In the .h file:
@interface Mapnnotations : NSObject
-(BOOL)isEqual:(id)otherObj;
...
@end

// In the .m file:
@implementation Mapnnotations
-(BOOL)isEqual:(id)otherObj {
    ... // Check if other is Mapnnotations, and compare the other instance
        // to this instance
    Mapnnotations *other = (Mapnnotations*)otherObj;
    // Instead of comparing unique identifiers, you could compare
    // a combination of other custom properties of your objects:
    return self.uniqueIdentifier == other.uniqueIdentifier;
}
@end

Note: when you implement your own isEqual: method, it is a good idea to implement the hash method as well. This would let you use the custom objects in hash sets and as NSDictionary keys.

Sign up to request clarification or add additional context in comments.

12 Comments

I'm fairly sure that I will always be comparing 2 MapAnnotations. Should I do a comparison of representative properties (say... the title?) looking at the method signature you provided in your answer, am a little baffled how I determine equality with only one parameter. Would I use self.title == other.title? Sorry... am dumb designer only pretending to be a programmer.
Don't forget to also implement a proper hash method. This should always be done when creating an isEqual: method on a class.
@jamesBurns If you have a unique property title, you can implement hash in a single line, too: -(NSUInteger)hash {return [self.title hash];}
Great walkthrough of isEqual: and hash implementation by Mike Ash: mikeash.com/pyblog/… @jamesBurns
@dasblinkenlight I would state that if you implement an isEqual: method in your class then you must implement a proper hash method too. It is a requirement that if two objects return YES when compared via isEqual:, then hash must return the same value for both objects. The default implementation of hash would violate this rule unless both objects were in fact the same object instance. So no, your program will not work properly if you implement isEqual: but not hash.
|
0

Or you could use an NSOrderedSet (or mutable if needed) which is designed to do all your set membership functions, as well as having all your index type functions you'd expect from an NSArray.

You can transform it to array with - array when you need actual an NSArray version where an enumerable won't work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.