提问



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


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


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

最佳参考


如果你不担心物体的顺序,你的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];

其它参考1


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


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


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


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

其它参考2


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


为了将其降低到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(N 2 )中运行,因为removeObjectAtIndex:可能在O(N)时间运行。 [59]


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


那么该怎么办?这取决于实际情况。如果你有一个大型阵列并且你只需要少量的重复数据,那么就地重复数据删除工作就可以正常工作,而且你不必建立一个重复的数组。如果你有一个阵列,你预计会有很多重复,那么构建一个单独的,去掉的阵列可能是最好的方法。这里的内容是大O符号仅描述算法的特征,它不会明确地告诉您哪种情况对于任何特定情况都是最佳的。

其它参考3


适用于OS X v10.7及更高版本。


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


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


以下是在Order中从NSArray中删除重复值的代码。

其它参考4


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


做就是了


NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];


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


NSArray *uniqueArray = orderedSet.array;


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


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


有关结帐,请参阅NSOrderedSet参考[60]

其它参考5


需要订单


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

其它参考6


这里我从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**];
    }
}

其它参考7


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


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


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





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


它应该完美。如果sortedSourceArray包含NSString s,NSNumber s和/或NSDate s的混合,则可能会崩溃。

其它参考8


有一个KVC对象操作员提供更优雅的解决方案uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];这是一个NSArray类别。 [61]

其它参考9


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


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


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

其它参考10


以下是从NSMutable Array中删除重复值的代码。 .it将适合您。 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

其它参考11


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

其它参考12


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


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

其它参考13


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


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


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