13

I am trying to figure out how to implement this in Objective-C.

I want to remove the strings in an NSArray that have appear more than once in the array.

At the end I want to have an array that only has the unique lines in an array (meaning that not just the duplicates are deleted but also the original string that matches the duplicates.)

For example if you had the following array:

NSArray *array = [NSArray arrayWithObjects:@"bob", @"frank", @"sarah", @"sarah", @"fred", @"corey", @"corey", nil];

I would want the new array to look like this:

@"bob", @"frank", @"fred"

4 Answers 4

24

Use an NSCountedSet:

NSCountedSet *countedSet = [NSCountedSet setWithArray:yourArray];
NSMutableArray *finalArray = [NSMutableArray arrayWithCapacity:[yourArray count]];

for(id obj in countedSet) {
   if([countedSet countForObject:obj] == 1) {
      [finalArray addObject:obj];
   }
}

@Caleb suggested adding a method to NSCountedSet called -objectsWithCount:,, which I've implemented here:

@interface NSCountedSet (JRCountedSetAdditions)

- (NSArray *) objectsWithCount:(NSUInteger) count;

@end

@implementation NSCountedSet (JRCountedSetAdditions) 

- (NSArray *) objectsWithCount:(NSUInteger) count {
   NSMutableArray *array = [NSMutableArray array];
   for(id obj in self) {
      if([self countForObject:obj] == count) {
        [array addObject:obj];
      }  
   }
   return [array copy];
}

@end

Once that's done, all you need is one line:

NSArray *finalArray = [[NSCountedSet setWithArray:yourArray] objectsWithCount:1];

By the way, this is type-agnostic, so this will work with any Objective-C object. :-)

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

6 Comments

Why use an enumerator? Just use fast enumeration!
One line: NSArray *finalArray = [[NSCountedSet setWithObjects:yourArray] allObjects];
@Caleb: If that would work, I would've done it that way. But alas, it does not as -allObjects returns unique objects (i.e. one of the dupes), while the logic behind this is that we don't want the dupes either.
@Caleb: So, with your suggestion, the result would be: @"frank", @"corey", @"fred", @"bob", @"sarah".
@Jacob: Gotcha. I missed the requirement to remove the duplicated string as well as the duplicates. ;-) Given that, it'd be nice to take your for loop and put it in a -objectsWithCount: method on NSCountedSet.
|
10

One liner : uniqueArray = [[NSSet setWithArray:duplicateArray] allObjects]; if you don't care about the ordering :D

2 Comments

I was here to answer that!! Happy to see my answer is already here +1!! - (NSArray*)removeDuplicates:(NSArray*)list { return [[NSSet setWithArray: list] allObjects]; }
[WARNING], Update: No longer surprised haha, after a buggy afternoon I learned the hard way, this code is not guaranteed to retain the order of your array. Use [[NSOrderedSet ordereredSetWithArray:duplicateArray] array] instead.
4

A slightly different approach from Jacob's:

NSArray *array = [NSArray arrayWithObjects:@"bob", @"frank", @"sarah", @"sarah", @"fred", @"corey", @"corey", nil];

NSCountedSet *namesSet = [[NSCountedSet alloc] initWithArray:array];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithCapacity:[array count]];

[namesSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop){
    if ([namesSet countForObject:obj] == 1) {
        [namesArray addObject:obj];
    }
}];

And

NSLog(@"old: %@\nNew: %@", array, namesArray);

gives:

2011-06-16 18:10:32.783 SetTest[1756:903] old: (
    bob,
    frank,
    sarah,
    sarah,
    fred,
    corey,
    corey
)
New: (
    frank,
    fred,
    bob
)

Blocks are your friends! And since NSCountedSet is a subclass of NSSet you can use the block methods that are available there.

7 Comments

@Abizem: I'm sorry, how on earth is that different other than the usage of -enumerateObjectsUsingBlock:?
@Abizem: And, technically speaking, it is more expensive to allocate a block on the stack than a scoped id reference.
It's different from your original answer that used an enumerator, and I am trying to show that blocks could also be used in this situation, and with a bit of thread safe code thrown in, this could be done concurrently.
@Abizem Looks like you have a couple of memory leaks there... ;)
Absolutely. I ran it in -applicationDidFinishLaunching:! Also, I use GC ;)
|
2

Here is the simplest approach to remove duplicate strings:

NSArray *array = [NSArray arrayWithObjects:@"bob", @"frank", @"sarah", @"sarah", @"fred", @"corey", @"corey", nil];
NSArray *distintStrings = [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.