[ACCEPTED]-The best way to remove duplicate values from NSMutableArray in Objective-C?-nsmutablearray
Your NSSet
approach is the best if you're not 10 worried about the order of the objects, but 9 then again, if you're not worried about 8 the order, then why aren't you storing them 7 in an NSSet
to begin with?
I wrote the answer 6 below in 2009; in 2011, Apple added NSOrderedSet
to 5 iOS 5 and Mac OS X 10.7. What had been an 4 algorithm is now two lines of code:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
If you 3 are worried about the order and you're running 2 on iOS 4 or earlier, loop over a copy of 1 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];
I know this is an old question, but there 4 is a more elegant way to remove duplicates 3 in a NSArray
if you don't care about the order.
If we use Object Operators from Key Value Coding we can do this:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
As AnthoPak also 2 noted it is possible to remove duplicates 1 based on a property. An example would be: @distinctUnionOfObjects.name
Yes, using NSSet is a sensible approach.
To 50 add to Jim Puls' answer, here's an alternative 49 approach to stripping duplicates while retaining 48 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 47 as Jim's but copies unique items to a fresh 46 mutable array rather than deleting duplicates 45 from the original. This makes it slightly 44 more memory efficient in the case of a large 43 array with lots of duplicates (no need to 42 make a copy of the entire array), and is 41 in my opinion a little more readable.
Note 40 that in either case, checking to see if 39 an item is already included in the target 38 array (using containsObject:
in my example, or indexOfObject:inRange:
in Jim's) doesn't 37 scale well for large arrays. Those checks 36 run in O(N) time, meaning that if you double 35 the size of the original array then each check will 34 take twice as long to run. Since you're 33 doing the check for each object in the array, you'll 32 also be running more of those more expensive 31 checks. The overall algorithm (both mine 30 and Jim's) runs in O(N2) time, which gets 29 expensive quickly as the original array 28 grows.
To get that down to O(N) time you 27 could use a NSMutableSet
to store a record of items 26 already added to the new array, since NSSet 25 lookups are O(1) rather than O(N). In other 24 words, checking to see whether an element 23 is a member of an NSSet takes the same time 22 regardless of how many elements are in the 21 set.
Code using this approach would look 20 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 19 wasteful though; we're still generating 18 a new array when the question made clear 17 that the original array is mutable, so we 16 should be able to de-dupe it in place and 15 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 14 Niyazov pointed out that my last answer actually runs 13 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 12 to do? It depends on the situation. If you've 11 got a large array and you're only expecting 10 a small number of duplicates then the in-place 9 de-duplication will work just fine and save 8 you having to build up a duplicate array. If 7 you've got an array where you're expecting 6 lots of duplicates then building up a separate, de-duped 5 array is probably the best approach. The 4 take-away here is that big-O notation only 3 describes the characteristics of an algorithm, it 2 won't tell you definitively which is best 1 for any given circumstance.
If you are targeting iOS 5+ (what covers 8 the whole iOS world), best use NSOrderedSet
. It removes 7 duplicates and retains the order of your 6 NSArray
.
Just do
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
You can now convert it back to a 5 unique NSArray
NSArray *uniqueArray = orderedSet.array;
Or just use the orderedSet 4 because it has the same methods like an 3 NSArray like objectAtIndex:
, firstObject
and so on.
A membership check 2 with contains
is even faster on the NSOrderedSet
than it would 1 be on an NSArray
For more checkout the NSOrderedSet Reference
Available in OS X v10.7 and later.
If you 3 are worried about the order,right way to 2 do
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
Here is the code of removing duplicates 1 values from NSArray in Order.
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);
0
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]];
}
}
0
Note that if you have a sorted array, you 20 don't need to check against every other 19 item in the array, just the last item. This 18 should be much faster than checking against 17 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 16 are also suggested require a lot less code, but 15 if you can't use an NSOrderedSet
for some reason, and 14 you have a sorted array, I believe my solution 13 would be the fastest. I'm not sure how 12 it compares with the speed of the NSOrderedSet
solutions. Also 11 note that my code is checking with isEqualToString:
, so 10 the same series of letters will not appear 9 more than once in newArray
. I'm not sure if the 8 NSOrderedSet
solutions will remove duplicates based 7 on value or based on memory location.
My 6 example assumes sortedSourceArray
contains just NSString
s, just NSMutableString
s, or 5 a mix of the two. If sortedSourceArray
instead contains 4 just NSNumber
s or just NSDate
s, you can replace
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
with
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
and 3 it should work perfectly. If sortedSourceArray
contains 2 a mix of NSString
s, NSNumber
s, and/or NSDate
s, it will probably 1 crash.
There's a KVC Object Operator that offers 1 a more elegant solution uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Here's an NSArray category.
One more simple way you can try out which 4 will not add duplicate Value before adding 3 object in array:-
//Assume mutableArray is 2 allocated and initialize and contains some 1 value
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Remove duplicate values from NSMutableArray 1 in Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
Here is the code of removing duplicates values from NSMutable Array..it will work for you. myArray is your Mutable 2 Array that you want to remove duplicates 1 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
Using Orderedset
will do the trick. This will keep 3 the remove duplicates from the array and 2 maintain order which sets normally doesn't 1 do
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.