从NSMutableArray中删除重复项

4

我有一个问题,需要从数组中删除重复的对象。 我已经尝试了以下方法:

noDuplicates = _personalHistory.personalHistory;

for (int i=[noDuplicates count]-1; i>0; i--) {
    if ([noDuplicates indexOfObject: [noDuplicates objectAtIndex: i]]<i)
        [noDuplicates removeObjectAtIndex: i];
}


for (PersonalHistory_artikels *e in _personalHistory.personalHistory) {
    if (![noDuplicates containsObject:e]) {
        NSLog(@"Dubplicates");
        [noDuplicates addObject:e];
    }
}


for (i=0; i<_personalHistory.personalHistory.count; i++) {
    PersonalHistory_artikels *test = [_personalHistory.personalHistory objectAtIndex:i];
    for (j=0; j<_personalHistory.personalHistory.count; j++) {
        PersonalHistory_artikels *test2 = [_personalHistory.personalHistory objectAtIndex:j];
        if (! [test.nieuwsTITLE_personal isEqual:test2.nieuwsTITLE_personal]) {
            NSLog(@"Add test = %@", test.nieuwsTITLE_personal);
            [noDuplicates addObject:test];
        }
    }
}

但是以上的方法都没有给我正确的数组。最后一个方法是最好的,但它仍然显示了重复的值。有人可以帮我解决这个问题吗? 非常感谢。


你能否展示一下你的 PersonalHistory_artikels 类的 @interface - octy
9个回答

38

只需将数组转换为NSSet,然后再转回来即可。由于设计原因,集合不能有重复项。

编辑:

请注意,集合没有排序顺序。因此,您可以使用更便宜的方法放弃排序,或者使用略微更昂贵的操作以保持排序。

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

-1. 那也是我的第一个想法,但是集合不能保证项目的顺序。 - zoul
1
是的,但如果你要从数组中删除重复项,你将无法保证项目的顺序,因为你只能保留每个项目的第一个或最后一个实例。 - Alexsander Akers
取决于发帖人的意图。他的数组被称为“history”,因此很可能他正在尝试将类似于“aaabbca”的东西转换为“abc”。这就是为什么我认为朴素的解决方案更好。但你是对的,集合也可以,只是最好警告一下结果项的顺序。 - zoul
我尝试了一下,但是收到一个“Program received signal: “EXC_BAD_ACCESS”.”的信息。 - user750079

18

在OS X 10.7和iOS 5.0及更高版本中:

newArray = [[NSOrderedSet orderedSetWithArray:oldArray] array];

1
我对这个解决方案的一个问题是,顺序不一定保持不变(这让我困扰了一段时间)。 - J.R.
1
我必须承认我实际上从未尝试过。您能否详细说明不维护顺序的情况? - hatfinch

6
如果您想保持排序,可以按照以下方式操作:
@interface NSArray (OrderedDuplicateElimination)

- (NSArray *)arrayByEliminatingDuplicatesMaintainingOrder
{
  NSMutableSet *addedObjects = [NSMutableSet set];
  NSMutableArray *result = [NSMutableArray array];

  for (id obj in self) {
    if (![addedObjects containsObject:obj]) {
      [result addObject:obj];
      [addedObjects addObject:obj];
    }
  }

  return result;
}

@end

这种解决方案的计算复杂度比目前大多数建议的方案都要低;对于一个包含N个元素且其中M个是唯一的数组,其最坏情况下的复杂度应为O(N log M),而不是O(N^2)。然而,由于这种方法确实有一些额外的开销,对于短数组来说,更简单的解决方案可能会更快一些。
当然,这也要求你的-isEqual:和-hash方法被正确实现。

与我所使用的答案类似,但我会从一个空集开始,并将不在集合中的对象添加到集合和新数组中。 - JeremyP
@JeremyP 我已经更新了代码,反映了这个想法,因为我认为你的观点更好。有趣的是,反转 set 的意义确实避免了必须预先创建一个对象完整集合,虽然这也意味着你不能轻松地测试完成情况;但是,由于 set 已经必须迭代整个数组,所以总体上可能并没有获胜。 - al45tair

3
这个类别怎么样?
@implementation NSArray (Unique)

- (NSArray*) arrayByDroppingDuplicates
{
    NSMutableArray *tmp = [NSMutableArray array];
    for (id item in self)
        if (![tmp containsObject:item])
            [tmp addObject:item];
    return [NSArray arrayWithArray:tmp];
}

@end

您可以像这样使用它:
NSArray *items = [NSArray arrayWithObjects:@"foo", @"bar", @"foo", nil];
NSArray *unique = [items arrayByDroppingDuplicates]; // [@"foo", @"bar"]

在这种情况下,您可能会遇到相等性检查的问题,请参阅Octy的答案。(确定源数组不是nil吗?) - zoul
这个方法的时间复杂度为O(N^2),因为-containsObject:是O(N)。也就是说,执行该方法所需的时间会随着数组长度的平方而增加(这非常糟糕)。 - al45tair
好的建议。这可能不是一个问题,直到有几百个项目(在我的iMac上为1000个随机生成的字符串以0.03秒完成),但知道这一点很好。 - zoul
嗨,我尝试了这个选项:但是我收到了一个警告:NSMutableArray可能无法响应removeDuplicates。(我也尝试过NSArray)。 - user750079
展示给我们新的代码,这样我们就不必猜测哪里出了问题。 - zoul

2
我认为你的问题在于如何定义PersonalHistory_artikels对象的相等性。
无论你使用什么算法来删除数组中的重复项,一定要提供足够的-isEqual:-hash方法实现。请参考苹果文档中的这两个方法,特别是以下段落:
“如果两个对象是相等的(由isEqual:方法确定),那么它们必须具有相同的哈希值。如果你在子类中定义了哈希方法并打算将该子类的实例放入集合中,则最后一点尤为重要。
如果向使用哈希值来确定对象在集合中的位置的集合中添加可变对象,则对象的哈希方法返回的值在对象在集合中时不得更改。因此,哈希方法要么不能依赖于任何对象的内部状态信息,要么必须确保对象的内部状态信息在对象在集合中时不会更改。例如,可变字典可以放在哈希表中,但在其中时不得更改它。(注意,很难知道一个给定的对象是否在集合中。)”
希望这可以帮到你。

1

从数组中删除重复元素的最佳方法:

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

1
与Alexsander Akers提供的解决方案相同。 - Smilin Brian

1
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];

这个方法的时间复杂度也是O(N^2),但在某些情况下可能比另一种O(N^2)的方法更快,因为它至少避免了考虑已经处理过的项目。然而,它的开销更大,因为它首先要复制整个输入数组。 - al45tair

0

不需要创建 NSSet 或其他东西。 尝试这个

noDuplicates = [orgArray valueForKeyPath:@"@distinctUnionOfObjects.self"];

0

对于包含自定义对象的数组:

    NSArray *arrUnique = [_pSessionArr valueForKeyPath:@"@distinctUnionOfObjects.self.title"];
    [_pSessionArr removeObjectsInRange:NSMakeRange(arrUnique.count, _pSessionArr.count-arrUnique.count )];

注意:_pSessionArr是一个可变数组的名称,其中包含一个自定义类对象,该对象具有名为title的属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接