在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
只包含NSNumber
或NSDate
,则可以replace
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
同
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
它应该完美地工作。 如果sortedSourceArray
包含NSString
, NSNumber
和/或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不允许重复的值,所有的对象都返回一个数组