在 Objective-C 中去除 NSMutableArray 中重复值的最佳方法是什么?

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

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

uniquearray = [[NSSet setWithArray:yourarray] allObjects];
105957 次浏览

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

我在2009年写下了答案; 2011年,苹果在 iOS5和 Mac OS X 10.7中添加了 NSOrderedSet。曾经的算法现在变成了两行代码:

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

如果你担心顺序问题,并且你正在运行 iOS4或者更早的操作系统,那么循环一个数组的副本:

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 *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];

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

下面是从 NSMutableArray 中删除重复值的代码。.会对你有用的。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

是的,使用 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 的例子中使用 indexOfObject:inRange:)对于大型数组来说都没有很好的伸缩性。这些检查在 O (N)时间内运行,这意味着如果将原始数组的大小加倍,那么 每张支票将花费两倍的时间来运行。因为要对数组中的每个对象进行检查,所以还要运行更多的昂贵检查。整个算法(包括 mine 和 Jim 的算法)在 O (N2)时间内运行,随着原始数组的增长,代价会迅速增加。

为了减少到 O (N)时间,您可以使用 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)性能。)

那么,该怎么办?这要看情况。如果你有一个很大的数组,并且你只希望有少量的重复数据,那么就地删除重复数据就可以很好地工作,并且节省你建立一个重复数组的时间。如果您有一个需要大量重复的数组,那么构建一个单独的、去欺骗的数组可能是最好的方法。这里要说明的是,big-O 符号只描述了算法的特征,它不会明确地告诉你在任何给定的情况下哪个是最好的。

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

如果你担心订单,正确的做法

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

下面是按顺序从 NSArray 中删除重复值的代码。

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

照做就是了

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

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

NSArray *uniqueArray = orderedSet.array;

或者仅仅使用 orderedSet,因为它具有与 NSArray (如 objectAtIndex:firstObject等)相同的方法。

使用 contains的会员资格检查在 NSOrderedSet上甚至比在 NSArray上更快

了解更多关于 NSOrderedSet 引用的信息

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

// 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只包含 NSStringNSMutableString或两者的混合。如果 sortedSourceArray只包含 NSNumberNSDate,则可以替换

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

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

如果 sortedSourceArray包含 NSStringNSNumber和/或 NSDate的混合物,它可能会崩溃。

我知道这是一个老问题,但有一个更优雅的方式来删除重复的 NSArray 如果你不在乎命令的话

如果我们使用 来自键值编码的对象运算符,我们可以这样做:

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

安托帕克还指出,可以根据属性删除重复项。例如: @distinctUnionOfObjects.name

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

您可以尝试一种更简单的方法,在数组中添加对象之前不添加重复的 Value:-

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

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

需要秩序

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 中删除了重复的名称值,并将 result 存储在 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]];
}
}

使用 Orderedset就可以了。这将保持从数组中删除副本并维持通常不会设置的顺序

在 Objective-C 中删除 NSMutableArray 中的重复值

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