在NSArray中删除所有具有重复字符串的项

13

我正在尝试在Objective-C中实现这个功能。

我想要从一个NSArray中删除出现多次的字符串。

最终我希望得到的是一个只包含唯一行的数组(意味着不仅要删除重复的字符串,还要删除与重复项匹配的原始字符串)。

例如,如果您有以下数组:

NSArray *array = [NSArray arrayWithObjects:@"bob", @"frank", @"sarah", @"sarah", @"fred", @"corey", @"corey", nil];

我希望新数组看起来像这样:

@"bob", @"frank", @"fred"
4个回答

24

使用 NSCountedSet

NSCountedSet *countedSet = [NSCountedSet setWithArray:yourArray];
NSMutableArray *finalArray = [NSMutableArray arrayWithCapacity:[yourArray count]];

for(id obj in countedSet) {
   if([countedSet countForObject:obj] == 1) {
      [finalArray addObject:obj];
   }
}

@Caleb 提议在NSCountedSet中添加一个名为-objectsWithCount:的方法,我已经实现了这个方法:

@interface NSCountedSet (JRCountedSetAdditions)

- (NSArray *) objectsWithCount:(NSUInteger) count;

@end

@implementation NSCountedSet (JRCountedSetAdditions) 

- (NSArray *) objectsWithCount:(NSUInteger) count {
   NSMutableArray *array = [NSMutableArray array];
   for(id obj in self) {
      if([self countForObject:obj] == count) {
        [array addObject:obj];
      }  
   }
   return [array copy];
}

@end

完成这一步后,您只需要一行代码

NSArray *finalArray = [[NSCountedSet setWithArray:yourArray] objectsWithCount:1];

顺便说一句,这是与类型无关的,因此可以与任何Objective-C对象一起使用。:-)


2
为什么要使用枚举器?直接使用快速枚举不就好了! - Itai Ferber
1
一行代码:NSArray *finalArray = [[NSCountedSet setWithObjects:yourArray] allObjects]; - Caleb
1
@Caleb:如果那样能行,我早就这么做了。但不幸的是,-allObjects返回唯一对象(即重复项中的一个),而背后的逻辑是我们也不想要重复项。 - Jacob Relkin
@Caleb:所以,根据你的建议,结果将是:@"frank", @"corey", @"fred", @"bob", @"sarah" - Jacob Relkin
嘿 - 你编辑了你的答案!这让我的回答有些不那么相关;) - Abizern
1
@Jacob:明白了,我错过了删除重复字符串以及重复项的要求。;-) 鉴于此,将您的for循环放入NSCountedSet的-objectsWithCount:方法中会很好。 - Caleb

10

一句话说明:如果你不在意顺序,可以使用以下代码将重复的数组去重:uniqueArray = [[NSSet setWithArray:duplicateArray] allObjects];


我来回答这个问题了!很高兴看到我的答案已经在这里了 +1!!
  • (NSArray*)removeDuplicates:(NSArray*)list { return [[NSSet setWithArray: list] allObjects]; }
- Deepukjayan
[警告],更新:不再感到惊讶哈哈,在一个有缺陷的下午之后,我吃了亏,这段代码不能保证保留您的数组顺序。请改用[[NSOrderedSet ordereredSetWithArray:duplicateArray] array] - Albert Renshaw

4

Jacob稍有不同的方法:

NSArray *array = [NSArray arrayWithObjects:@"bob", @"frank", @"sarah", @"sarah", @"fred", @"corey", @"corey", nil];

NSCountedSet *namesSet = [[NSCountedSet alloc] initWithArray:array];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithCapacity:[array count]];

[namesSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop){
    if ([namesSet countForObject:obj] == 1) {
        [namesArray addObject:obj];
    }
}];

NSLog(@"old: %@\nNew: %@", array, namesArray);

给出:

2011-06-16 18:10:32.783 SetTest[1756:903] old: (
    bob,
    frank,
    sarah,
    sarah,
    fred,
    corey,
    corey
)
New: (
    frank,
    fred,
    bob
)

块是你的好朋友!由于NSCountedSetNSSet的子类,因此您可以使用可用的块方法。


@Abizem:对不起,除了使用-enumerateObjectsUsingBlock:之外,这有什么不同吗? - Jacob Relkin
@Abizem: 从技术上讲,在堆栈上分配一个块比作用域id引用更昂贵。 - Jacob Relkin
2
这与您原来使用枚举器的答案不同,我试图展示在这种情况下也可以使用块,并加入一些线程安全的代码,以实现并发。 - Abizern
我认为你不需要在传递的块中使用if语句块。在块体内部使用[namesArray addObject:obj]; 就足够满足我们的需求了。 - Devarshi
if块的目的是过滤掉重复项,而不仅仅是删除重复项。请查看问题中的要求。 - Abizern
显示剩余2条评论

2

这里是最简单的去除重复字符串的方法:

NSArray *array = [NSArray arrayWithObjects:@"bob", @"frank", @"sarah", @"sarah", @"fred", @"corey", @"corey", nil];
NSArray *distintStrings = [array valueForKeyPath:@"@distinctUnionOfObjects.self"];

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