如何在Objective-C中从NSMutableArray
中删除重复的值(NSString
)?
这是最简单且正确的方式吗?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
如何在Objective-C中从NSMutableArray
中删除重复的值(NSString
)?
这是最简单且正确的方式吗?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
如果您不担心对象的顺序,那么您的NSSet
方法是最好的选择,但是,如果您不关心顺序,为什么不一开始就将它们存储在NSSet
中呢?
我在2009年撰写了下面的答案;在2011年,苹果公司推出了NSOrderedSet
,可用于iOS 5和Mac OS X 10.7。曾经需要算法来实现的现在只需要两行代码:
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];
[NSOrderedSet orderedSetWithArray:array];
。然后,您可以通过 array = [orderedSet allObjects];
获取一个数组,或者在开始时直接使用 NSOrderedSet
而不是 NSArray
。 - Regexident[orderedSet allObjects]
替换为[orderedSet array]
! - inketNSArray
,并且应该创建临时的NSMutableArray
。在你的例子中,你的做法是相反的。 - Vyachaslav Gerchicov我知道这是一个老问题,但是有一种更加优雅的方法可以在 NSArray
中删除重复项如果你不关心顺序的话。
如果我们使用Key Value Coding中的对象运算符,我们可以这样做:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
正如AnthoPak所指出的那样,可以根据属性删除重复项。例如:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
一样使用,通过自定义对象数组的属性来删除重复项。例如 @distinctUnionOfObjects.name
。 - AnthoPak使用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];
}
}
containsObject:
或在Jim中使用indexOfObject:inRange:
)对于大型数组不会很好地扩展。这些检查在O(N)时间内运行,这意味着如果您将原始数组的大小加倍,则每个检查将需要运行两倍的时间。由于您正在对数组中的每个对象进行检查,因此您还将运行更多的这些更昂贵的检查。总体算法(我的和Jim的)以O(N 2 )时间运行,随着原始数组的增长,成本迅速增加。NSMutableSet
来存储已添加到新数组的记录,因为NSSet查找是O(1),而不是O(N)。换句话说,检查元素是否为NSSet的成员需要相同的时间,而不管集合中有多少元素。NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
但这种方法仍然有些浪费;问题明确表示原始数组是可变的,因此我们应该能够就地进行去重并节省一些内存。可以尝试以下代码:
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++;
}
}
更新:Yuri Niyazov 指出,我的上一个答案实际上运行时间为O(N2),因为removeObjectAtIndex:
可能需要O(N)的时间。
(他说“可能”是因为我们不确定它是如何实现的;但一种可能的实现方式是,在删除索引X处的对象后,该方法会遍历从索引X + 1到数组中的最后一个对象的每个元素,并将它们移动到前一个索引。如果是这种情况,那么性能确实是O(N)。)
那么,该怎么办呢?这取决于情况。如果您有一个大数组,并且只期望有少量重复项,则原地去重将完美地工作,并节省构建重复数组的时间。如果您有一个数组,其中有很多重复项,则构建一个单独的去重数组可能是最好的方法。这里的要点是,大O符号仅描述算法的特征,它不能明确告诉您在任何给定情况下哪种方法最好。
NSOrderedSet
。它可以去重并保留您的 NSArray
的顺序。NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
现在,您可以将其转换回唯一的NSArray。
NSArray *uniqueArray = orderedSet.array;
或者直接使用 NSOrderedSet
,因为它具有与 NSArray
相同的方法,例如 objectAtIndex:
、firstObject
等等。
在 NSOrderedSet
上进行成员检查,甚至比在 NSArray
上进行检查更快。
欲了解更多,请参阅 NSOrderedSet 参考文献。
适用于OS X v10.7及更高版本。
如果您担心顺序,正确的做法是:
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
这里是按顺序从NSArray中删除重复值的代码。
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]];
}
}
您可以尝试另一种简单的方法,在将对象添加到数组之前不会添加重复值:
//假设mutableArray已分配和初始化,并包含一些值
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
在Objective-C中从NSMutableArray中删除重复值
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
// 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
答案需要更少的代码,但是如果由于某些原因不能使用NSOrderedSet
,并且您有一个已排序的数组,我相信我的解决方案将是最快的。我不确定它与NSOrderedSet
解决方案的速度相比如何。另请注意,我的代码正在使用isEqualToString:
进行检查,因此相同的字母序列不会在newArray
中出现多次。我不确定NSOrderedSet
解决方案是否基于值或基于内存位置删除重复项。
我的示例假定sortedSourceArray
仅包含NSString
,仅包含NSMutableString
,或两者混合。如果sortedSourceArray
代替只包含NSNumber
或只包含NSDate
,则可以替换
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
它应该完美运行。如果sortedSourceArray
包含了混合的NSString
,NSNumber
和/或NSDate
,那么它很可能会崩溃。