在Objective-C中从NSMutableArray中删除重复值的最好方法是什么?

在Objective-C中从NSMutableArray中删除重复值( NSString )的最好方法是什么?

这是最简单和正确的方法吗?

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

如果您不担心对象的顺序,那么您的NSSet方法是最好的,但是如果您不担心顺序,那么为什么不将它们存储在NSSet中呢?

我在2009年写了下面的答案。 在2011年,苹果公司在iOS 5和Mac OS X 10.7中增加了NSOrderedSet 。 现在algorithm是现在两行代码:

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

如果您担心订单,并且您正在iOS 4或更低版本上运行,请循环访问该数组的副本:

 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]; 

我知道这是一个古老的问题,但是如果你不关心这个命令 ,有一个更好的方法来删除NSArray中的重复项。

如果我们使用Key Value Coding中的Object Operators,我们可以这样做:

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

是的,使用NSSet是一个明智的方法。

要添加到Jim Puls的答案,这里是另一种方法来剥离重复同时保留顺序:

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

这与Jim的方法基本相同,但将独特的项目复制到新鲜的可变数组中,而不是从原始数据中删除重复项。 这使得在有大量重复的大型数组的情况下(不需要复制整个数组),在内存中的效率稍高一些,而且在我看来,它更具可读性。

请注意,在这两种情况下,检查目标数组中是否已经包含一个项目(在我的示例中使用containsObject:或者在Jim's中使用indexOfObject:inRange:对于大数组来说不能很好地扩展。 这些检查在O(N)时间内运行,这意味着如果您将原始数组的大小加倍,那么每次检查将花费两倍的时间运行。 由于您正在检查数组中的每个对象,因此您还将运行更多更昂贵的检查。 整个algorithm(包括我的和吉姆的)在O(N 2 )时间运行,随着原始数组的增长,这个时间变得很快。

为了把它放到O(N)时间,你可以使用一个NSMutableSet来存储已经添加到新数组的项目logging,因为NSSet查找是O(1)而不是O(N)。 换句话说,无论组中有多less个元素,检查一个元素是否是NSSet的成员都需要相同的时间。

使用这种方法的代码看起来像这样:

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

尽pipe如此,这似乎仍然有点浪费。 当问题明确表示原始数组是可变的时候,我们仍然在生成一个新的数组,所以我们应该能够解决它,并保存一些内存。 像这样的东西:

 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++; } } 

更新 :尤里Niyazov 指出 ,我的最后一个答案实际上运行在O(N 2 ),因为removeObjectAtIndex:可能运行在O(N)时间。

(他说“很可能”,因为我们不知道它是如何实现的;但是一个可能的实现是在删除索引X处的对象之后,该方法循环遍历索引X + 1中的每个元素到数组中的最后一个对象,把它们移动到前一个索引,如果是这样的话,那么的确是O(N)performance。

那么该怎么办? 这取决于实际情况。 如果你有一个庞大的数组,而你只需要less量的重复数据,那么就地的重复数据删除就可以正常工作,并且不需要build立一个重复的数组。 如果你有一个数组,那么你需要大量的重复数据,那么build立一个单独的,可选的数组可能是最好的方法。 这里的外延是大O符号只描述一个algorithm的特征,它不会明确地告诉你哪个是最适合于任何给定的情况。

在OS X v10.7及更高版本中可用。

如果你担心顺序,正确的做法

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

这里是在Order中从NSArray中删除重复值的代码。

如果你的目标iOS 5+(覆盖整个iOS世界),最好使用NSOrderedSet 。 它删除重复项,并保留您的NSArray的顺序。

做就是了

 NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray]; 

您现在可以将其转换回唯一的NSArray

 NSArray *uniqueArray = orderedSet.array; 

或者只是使用orderedSet,因为它有一个类似于objectAtIndex:firstObject等NSArray的方法。

contains的成员资格检查在NSOrderedSet比在NSArray上更快

更多检查NSOrderedSet参考

需要订单

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

或者不需要订单

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

在这里,我从mainArray中删除重复的名称值,并将结果存储在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]]; } } 

有一个KVC对象运算符,提供了一个更优雅的解决schemeuniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"]; 这是一个NSArray类别 。

一个更简单的方法,你可以尝试在数组中添加对象之前不会添加重复的值: –

//假设mutableArray被分配并初始化并包含一些值

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

请注意,如果您有一个sorting的数组,则不需要检查数组中的每一个其他项,只是最后一项。 这应该比检查所有项目要快得多。

 // 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]]; } } 

它看起来像NSOrderedSet答案也build议需要less得多的代码,但如果你不能使用NSOrderedSet由于某种原因,你有一个sorting的数组,我相信我的解决scheme将是最快的。 我不确定它与NSOrderedSet解决scheme的速度相比如何。 另外请注意,我的代码正在使用isEqualToString:检查,所以在newArray不会出现同一系列的字母。 我不确定NSOrderedSet解决scheme是否会根据值或基于内存位置删除重复项。

我的例子中假设sortedSourceArray只包含NSString s,只是NSMutableString s或两者的组合。 如果sortedSourceArray只包含NSNumberNSDate ,则可以replace

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

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

它应该完美地工作。 如果sortedSourceArray包含NSStringNSNumber和/或NSDate的混合,它可能会崩溃。

这里是从NSMutablearrays中删除重复值的代码。 它会为你工作。 myArray是你想要删除重复值的可变数组..

 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 

使用Orderedset会做的伎俩。 这将保持从数组中删除重复,并保持通常不会做的顺序

只需使用这个简单的代码:

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

因为nsset不允许重复的值,所有的对象都返回一个数组