12

I have an array with custom objects. Each array item has a field named "name". Now I want to remove duplicate entries based on this name value.

How should I go about achieving this?

3
  • Are the items in an Objective-C container like NSArray, or C/C++ (e.g., std::vector or a C array)? Commented Oct 24, 2010 at 7:38
  • sorry removed the tag, its objective c array with custom objects... "name" is one of its member variable, I want to filter based on this "name" value. Commented Oct 24, 2010 at 7:45
  • Check my answer with code example: stackoverflow.com/a/32136313/988169 Commented Aug 21, 2015 at 8:59

8 Answers 8

30

I do not know of any standard way to to do this provided by the frameworks. So you will have to do it in code. Something like this should be doable:

NSArray* originalArray = ... // However you fetch it
NSMutableSet* existingNames = [NSMutableSet set];
NSMutableArray* filteredArray = [NSMutableArray array];
for (id object in originalArray) {
   if (![existingNames containsObject:[object name]]) {
      [existingNames addObject:[object name]];
      [filteredArray addObject:object];
   }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks PeyloW your answer was right on spot... thumbs up... I wish you best of luck in your endeavors....
Shouldn't it be [object objectForKey:@"name"]; in side that loop?
Really nice solution
Got SIGABRT on this in 2017 O_O
10

You might have to actually write this filtering method yourself:

@interface NSArray (CustomFiltering)
@end

@implementation NSArray (CustomFiltering) 

- (NSArray *) filterObjectsByKey:(NSString *) key {
   NSMutableSet *tempValues = [[NSMutableSet alloc] init];
   NSMutableArray *ret = [NSMutableArray array];
   for(id obj in self) {
       if(! [tempValues containsObject:[obj valueForKey:key]]) {
            [tempValues addObject:[obj valueForKey:key]];
            [ret addObject:obj];
       }
   }
   [tempValues release];
   return ret;
}

@end

6 Comments

its not a string array its a custom object array.... with a string name as the property... I want to filter based in this name property
@Jacob: Wrote my answer just as you edited yours. You really should use an NSMutableSet instead of a NSMutableArray for lookups, the performance boost from a hash lookup is quite allot better than a linear search.
A couple of comments. First, you should just use [NSMutableSet set] instead of alloc/init+release. Second, you should use -member: instead of -containsObject:. -containsObject: is documented as returning whether the given object is present in the set, without defining "present". It is reasonable to assume it uses pointer equality. -member: is documented as using -isEqual:, which is what you actually want to test with.
Thanks all of you guys... your answers & comments really helped me. Thumbs up for all of you...
@Kevin, -containsObject: looks for membership the same way as -member: does. The only diff is returning BOOL versus the object itself.
|
7

I know this is an old question but here is another possibility, depending on what you need.

Apple does provide a way to do this -- Key-Value Coding Collection Operators.

Object operators let you act on a collection. In this case, you want:

@distinctUnionOfObjects

The @distinctUnionOfObjects operator returns an array containing the distinct objects in the property specified by the key path to the right of the operator.

NSArray *distinctArray = [arrayWithDuplicates valueForKeyPath:@"@distinctUnionOfObjects.name"];

In your case, though, you want the whole object. So what you'd have to do is two-fold: 1) Use @distinctUnionOfArrays instead. E.g. If you had these custom objects coming from other collections, use @distinctUnionOfArray.myCollectionOfObjects 2) Implement isEqual: on those objects to return if their .name's are equal

Comments

2

I'm going to get flak for this...

You can convert your array into a dictionary. Not sure how efficient this is, depends on the implementation and comparison call, but it does use a hash map.

//Get unique entries
NSArray *myArray = @[@"Hello", @"World", @"Hello"];
NSDictionary *uniq = [NSDictionary dictionaryWithObjects:myArray forKeys:myArray];
NSLog(@"%@", uniq.allKeys);

*Note, this may change the order of your array.

2 Comments

Hehehehe I LOVE this!
Edit: well I just spent the last 15 minutes fixing a bug in my code. Using this NSDictionary method will NOT keep the ORDER of your array. All my items were scrambled. That's what I get for using hacky wordk-arounds where OP prefaces "I'm going to get flak for this..." lol
1

If you'd like your custom NSObject subclasses to be considered equal when their names are equal you may implement isEqual: and hash. This will allow you to add of the objects to an NSSet/NSMutableSet (a set of distinct objects).

You may then easily create a sorted NSArray by using NSSet's sortedArrayUsingDescriptors:method.

MikeAsh wrote a pretty solid piece about implementing custom equality: Friday Q&A 2010-06-18: Implementing Equality and Hashing

Comments

1

If you are worried about the order

NSArray * newArray =
        [[NSOrderedSet orderedSetWithArray:oldArray] array]; **// iOS 5.0 and later** 

Comments

1

It is quite simple in one line

NSArray *duplicateList = ... 

If you don't care about elements order then (unordered)

NSArray *withoutDUP1 = [[NSSet setWithArray:duplicateList] allObjects];

Keep the elements in order then (ordered)

NSArray *withoutDUP2 = [[NSOrderedSet orderedSetWithArray:duplicateList] array];

1 Comment

But how is your code filtering based on field "name" ?
0

Implement isEqual to make your objects comparable:

@interface SomeObject (Equality)
@end

@implementation SomeObject (Equality)

- (BOOL)isEqual:(SomeObject*)other
{
    return self.hash == other.hash;
}

- (NSUInteger)hash
{
    return self.name;///your case
}

@end

How to use:

- (NSArray*)distinctObjectsFromArray:(NSArray*)array
{
    return [array valueForKeyPath:@"@distinctUnionOfObjects.self"];
}

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.