The best way to remove duplicate values from NSMutableArray in Objective-C?

IosObjective CNsmutablearray

Ios Problem Overview


The best way to remove duplicate values (NSString) from NSMutableArray in Objective-C?

Is this the easiest and right way to do it?

uniquearray = [[NSSet setWithArray:yourarray] allObjects];

Ios Solutions


Solution 1 - Ios

Your NSSet approach is the best if you're not worried about the order of the objects, but then again, if you're not worried about the order, then why aren't you storing them in an NSSet to begin with?

I wrote the answer below in 2009; in 2011, Apple added NSOrderedSet to iOS 5 and Mac OS X 10.7. What had been an algorithm is now two lines of code:

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];

If you are worried about the order and you're running on iOS 4 or earlier, loop over a copy of the array:

NSArray *copy = [mutableArray copy];
NSInteger index = [copy count] - 1;
for (id object in [copy reverseObjectEnumerator]) {
    if ([mutableArray indexOfObject:object inRange:NSMakeRange(0, index)] != NSNotFound) {
        [mutableArray removeObjectAtIndex:index];
    }
    index--;
}
[copy release];

Solution 2 - Ios

I know this is an old question, but there is a more elegant way to remove duplicates in a NSArray if you don't care about the order.

If we use [Object Operators from Key Value Coding][1] we can do this:

uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];

As AnthoPak also noted it is possible to remove duplicates based on a property. An example would be: @distinctUnionOfObjects.name [1]: http://nshipster.com/kvc-collection-operators/

Solution 3 - Ios

Yes, using NSSet is a sensible approach.

To add to Jim Puls' answer, here's an alternative approach to stripping duplicates while retaining order:

// Initialise a new, empty mutable array 
NSMutableArray *unique = [NSMutableArray array];
    
for (id obj in originalArray) {
    if (![unique containsObject:obj]) {
        [unique addObject:obj];
    }
}
    

It's essentially the same approach as Jim's but copies unique items to a fresh mutable array rather than deleting duplicates from the original. This makes it slightly more memory efficient in the case of a large array with lots of duplicates (no need to make a copy of the entire array), and is in my opinion a little more readable.

Note that in either case, checking to see if an item is already included in the target array (using containsObject: in my example, or indexOfObject:inRange: in Jim's) doesn't scale well for large arrays. Those checks run in O(N) time, meaning that if you double the size of the original array then each check will take twice as long to run. Since you're doing the check for each object in the array, you'll also be running more of those more expensive checks. The overall algorithm (both mine and Jim's) runs in O(N2) time, which gets expensive quickly as the original array grows.

To get that down to O(N) time you could use a NSMutableSet to store a record of items already added to the new array, since NSSet lookups are O(1) rather than O(N). In other words, checking to see whether an element is a member of an NSSet takes the same time regardless of how many elements are in the set.

Code using this approach would look something like this:

NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
    
for (id obj in originalArray) {
    if (![seen containsObject:obj]) {
        [unique addObject:obj];
        [seen addObject:obj];
    }
}

This still seems a little wasteful though; we're still generating a new array when the question made clear that the original array is mutable, so we should be able to de-dupe it in place and save some memory. Something like this:

NSMutableSet *seen = [NSMutableSet set];
NSUInteger i = 0;

while (i < [originalArray count]) {
    id obj = [originalArray objectAtIndex:i];

    if ([seen containsObject:obj]) {
        [originalArray removeObjectAtIndex:i];
        // NB: we *don't* increment i here; since
        // we've removed the object previously at
        // index i, [originalArray objectAtIndex:i]
        // now points to the next object in the array.
    } else {
        [seen addObject:obj];
        i++;
    }
}

UPDATE: Yuri Niyazov pointed out that my last answer actually runs in O(N2) because removeObjectAtIndex: probably runs in O(N) time.

(He says "probably" because we don't know for sure how it's implemented; but one possible implementation is that after deleting the object at index X the method then loops through every element from index X+1 to the last object in the array, moving them to the previous index. If that's the case then that is indeed O(N) performance.)

So, what to do? It depends on the situation. If you've got a large array and you're only expecting a small number of duplicates then the in-place de-duplication will work just fine and save you having to build up a duplicate array. If you've got an array where you're expecting lots of duplicates then building up a separate, de-duped array is probably the best approach. The take-away here is that big-O notation only describes the characteristics of an algorithm, it won't tell you definitively which is best for any given circumstance.

Solution 4 - Ios

If you are targeting iOS 5+ (what covers the whole iOS world), best use NSOrderedSet. It removes duplicates and retains the order of your NSArray.

Just do

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

You can now convert it back to a unique NSArray

NSArray *uniqueArray = orderedSet.array;

Or just use the orderedSet because it has the same methods like an NSArray like objectAtIndex:, firstObject and so on.

A membership check with contains is even faster on the NSOrderedSet than it would be on an NSArray

For more checkout the NSOrderedSet Reference

Solution 5 - Ios

Available in OS X v10.7 and later.

If you are worried about the order,right way to do

NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];

Here is the code of removing duplicates values from NSArray in Order.

Solution 6 - Ios

need order

NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);

or don't need order

NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);

Solution 7 - Ios

Here i removed duplicate name values from mainArray and store result in NSMutableArray(listOfUsers)

for (int i=0; i<mainArray.count; i++) {
    if (listOfUsers.count==0) {
        [listOfUsers addObject:[mainArray objectAtIndex:i]];
      
    }
   else if ([[listOfUsers valueForKey:@"name" ] containsObject:[[mainArray objectAtIndex:i] valueForKey:@"name"]])
    {  
       NSLog(@"Same object");
    }
    else
    {
        [listOfUsers addObject:[mainArray objectAtIndex:i]];
    }
}

Solution 8 - Ios

Note that if you have a sorted array, you don't need to check against every other item in the array, just the last item. This should be much faster than checking against all items.

// sortedSourceArray is the source array, already sorted
NSMutableArray *newArray = [[NSMutableArray alloc] initWithObjects:[sortedSourceArray objectAtIndex:0]];
for (int i = 1; i < [sortedSourceArray count]; i++)
{
    if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
    {
        [newArray addObject:[tempArray objectAtIndex:i]];
    }
}

It looks like the NSOrderedSet answers that are also suggested require a lot less code, but if you can't use an NSOrderedSet for some reason, and you have a sorted array, I believe my solution would be the fastest. I'm not sure how it compares with the speed of the NSOrderedSet solutions. Also note that my code is checking with isEqualToString:, so the same series of letters will not appear more than once in newArray. I'm not sure if the NSOrderedSet solutions will remove duplicates based on value or based on memory location.

My example assumes sortedSourceArray contains just NSStrings, just NSMutableStrings, or a mix of the two. If sortedSourceArray instead contains just NSNumbers or just NSDates, you can replace

if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])

with

if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)

and it should work perfectly. If sortedSourceArray contains a mix of NSStrings, NSNumbers, and/or NSDates, it will probably crash.

Solution 9 - Ios

There's a KVC Object Operator that offers a more elegant solution uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"]; Here's an NSArray category.

Solution 10 - Ios

One more simple way you can try out which will not add duplicate Value before adding object in array:-

//Assume mutableArray is allocated and initialize and contains some value

if (![yourMutableArray containsObject:someValue])
{
   [yourMutableArray addObject:someValue];
}

Solution 11 - Ios

Remove duplicate values from NSMutableArray in Objective-C

NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
    if([datelistArray indexOfObject:data.date] == NSNotFound)
    [datelistArray addObject:data.date];
}

Solution 12 - Ios

Here is the code of removing duplicates values from NSMutable Array..it will work for you. myArray is your Mutable Array that you want to remove duplicates values..

for(int j = 0; j < [myMutableArray count]; j++){
    for( k = j+1;k < [myMutableArray count];k++){
    NSString *str1 = [myMutableArray objectAtIndex:j];
    NSString *str2 = [myMutableArray objectAtIndex:k];
    if([str1 isEqualToString:str2])
        [myMutableArray removeObjectAtIndex:k];
    }
 } // Now print your array and will see there is no repeated value

Solution 13 - Ios

Using Orderedset will do the trick. This will keep the remove duplicates from the array and maintain order which sets normally doesn't do

Solution 14 - Ios

just use this simple code :

NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];

since nsset doesn't allow duplicate values and all objects returns an array

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTeo Choong PingView Question on Stackoverflow
Solution 1 - IosJim PulsView Answer on Stackoverflow
Solution 2 - IosTiago AlmeidaView Answer on Stackoverflow
Solution 3 - IosSimon WhitakerView Answer on Stackoverflow
Solution 4 - IoslukaswelteView Answer on Stackoverflow
Solution 5 - IosSultaniaView Answer on Stackoverflow
Solution 6 - IosMikeView Answer on Stackoverflow
Solution 7 - IosBibin JosephView Answer on Stackoverflow
Solution 8 - IosGeneralMikeView Answer on Stackoverflow
Solution 9 - IosPeterView Answer on Stackoverflow
Solution 10 - IosHussain ShabbirView Answer on Stackoverflow
Solution 11 - IosArvind PatelView Answer on Stackoverflow
Solution 12 - IosIHSAN KHANView Answer on Stackoverflow
Solution 13 - IosabhiView Answer on Stackoverflow
Solution 14 - IosDinesh619View Answer on Stackoverflow