150

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

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

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

14 回答 14

245

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

我在 2009 年写了下面的答案;2011 年,Apple 加入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];
于 2009-06-22T06:37:37.593 回答
78

NSArray 我知道这是一个老问题,但是如果您不关心 order,有一种更优雅的方法可以删除 a 中的重复项。

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

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

正如AnthoPak还指出的,可以根据属性删除重复项。一个例子是:@distinctUnionOfObjects.name

于 2013-10-15T14:44:01.627 回答
47

是的,使用 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:在我的示例中或indexOfObject:inRange:Jim 的示例中使用)对于大型数组都不能很好地扩展。这些检查在 O(N) 时间内运行,这意味着如果将原始数组的大小加倍,那么每次检查将花费两倍的时间来运行。由于您正在检查数组中的每个对象,因此您还将运行更多更昂贵的检查。整个算法(我的和 Jim 的)在 O(N 2 ) 时间内运行,随着原始数组的增长,它会很快变得昂贵。

要将其降低到 O(N) 时间,您可以使用 aNSMutableSet来存储已添加到新数组中的项目的记录,因为 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(N 2 ) 中运行,因为removeObjectAtIndex:可能在 O(N) 时间内运行。

(他说“可能”是因为我们不确定它是如何实现的;但一种可能的实现是,在删除索引 X 处的对象后,该方法会遍历从索引 X+1 到数组中最后一个对象的每个元素,将它们移动到上一个索引。如果是这样,那么这确实是 O(N) 性能。)

那么该怎么办?这取决于实际情况。如果您有一个大型数组并且您只期望少量重复,那么就地重复数据删除将工作得很好,并且您不必建立一个重复数组。如果您有一个预期有很多重复的数组,那么构建一个单独的、重复数据删除的数组可能是最好的方法。这里的要点是,大 O 表示法仅描述了算法的特征,它不会明确告诉您哪种方法最适合任何给定情况。

于 2012-06-11T06:25:29.993 回答
21

如果您的目标是 iOS 5+(涵盖整个 iOS 世界),最好使用NSOrderedSet. 它会删除重复项并保留您的NSArray.

做就是了

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

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

NSArray *uniqueArray = orderedSet.array;

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

在 上进行会员检查contains甚至NSOrderedSet比在NSArray

欲了解更多信息,请查看 NSOrderedSet 参考

于 2013-06-19T09:24:38.497 回答
19

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

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

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

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

于 2013-05-14T08:35:42.407 回答
7

需要订购

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);
于 2015-06-02T09:30:17.700 回答
3

在这里,我从 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]];
    }
}
于 2016-05-12T12:58:29.403 回答
1

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

// 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您不能使用 an并且您有一个排序数组,我相信我的解决方案将是最快的。NSOrderedSet我不确定它与NSOrderedSet解决方案的速度相比如何。另请注意,我的代码正在检查isEqualToString:,因此同一系列字母不会出现超过一次newArray。我不确定NSOrderedSet解决方案是否会根据值或内存位置删除重复项。

我的示例假设sortedSourceArray仅包含NSStrings、仅NSMutableStrings 或两者的混合。如果sortedSourceArray改为包含仅NSNumbers 或仅NSDates,则可以替换

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

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

它应该可以完美运行。如果sortedSourceArray包含NSStrings、NSNumbers 和/或NSDates 的混合,它可能会崩溃。

于 2013-08-14T13:55:12.357 回答
1

有一个 KVC Object Operator 可以提供更优雅的解决方案uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];Here's an NSArray category

于 2015-03-20T06:30:06.970 回答
1

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

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

if (![yourMutableArray containsObject:someValue])
{
   [yourMutableArray addObject:someValue];
}
于 2015-05-14T01:59:29.683 回答
1

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

NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
    if([datelistArray indexOfObject:data.date] == NSNotFound)
    [datelistArray addObject:data.date];
}
于 2018-05-23T13:52:52.550 回答
0

这是从 NSMutable 数组中删除重复值的代码。.它会为你工作。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
于 2012-05-15T09:15:32.667 回答
0

使用Orderedset就可以了。这将保留从数组中删除重复项并保持通常不做的设置的顺序

于 2016-06-08T17:42:07.937 回答
-3

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

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

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

于 2011-09-16T09:19:30.217 回答