基于Objective-C的属性从数组中删除重复项

12

我有一个包含自定义对象的数组。每个数组项都有一个名为“name”的字段。现在我想根据这个名称值删除重复的条目。

我该如何实现这个目标?


Objective-C容器中的项目是类似于NSArray,还是C/C++(例如std::vector或C数组)? - Marcelo Cantos
抱歉,我已经删除了标签。这是一个带有自定义对象的Objective-C数组。其中“name”是其成员变量之一,我想根据此“name”值进行过滤。 - Asad Khan
请查看我的代码示例,以验证我的答案:https://dev59.com/DlfUa4cB1Zd3GeqPEx89#32136313 - pkc456
8个回答

30

我不知道框架中是否有标准的方法可以做到这一点。因此,您需要在代码中完成它。类似这样的内容应该是可行的:

NSArray* originalArray = ... // However you fetch it
NSMutableSet* existingNames = [NSMutableSet set];
NSMutableArray* filteredArray = [NSMutableArray array];
for (id object in originalArray) {
   if (![existingNames containsObject:[object name]]) {
      [existingNames addObject:[object name]];
      [filteredArray addObject:object];
   }
}

谢谢PeyloW,你的回答非常准确...点赞...祝你在未来的努力中一切顺利... - Asad Khan
2
在那个循环里面不应该是[object objectForKey:@"name"];吗? - Pete
真的是一个非常好的解决方案。 - daleijn
2017年在这个上面收到了SIGABRT O_O - Albert Renshaw

10

你可能需要自己编写这个过滤方法:

@interface NSArray (CustomFiltering)
@end

@implementation NSArray (CustomFiltering) 

- (NSArray *) filterObjectsByKey:(NSString *) key {
   NSMutableSet *tempValues = [[NSMutableSet alloc] init];
   NSMutableArray *ret = [NSMutableArray array];
   for(id obj in self) {
       if(! [tempValues containsObject:[obj valueForKey:key]]) {
            [tempValues addObject:[obj valueForKey:key]];
            [ret addObject:obj];
       }
   }
   [tempValues release];
   return ret;
}

@end

它不是一个字符串数组,而是一个自定义对象数组...其中包含一个字符串名称作为属性...我想要根据这个名称属性进行过滤。 - Asad Khan
@Jacob:我刚在你编辑答案的时候写完了我的答案。你应该使用 NSMutableSet 而不是 NSMutableArray 进行查找,哈希查找的性能提升要比线性查找好得多。 - PeyloW
1
有几点需要注意。首先,你应该使用[NSMutableSet set]而不是alloc/init+release。其次,你应该使用-member:而不是-containsObject:-containsObject:的文档说明返回给定对象是否存在于集合中,但没有定义“存在”的含义。可以合理地假设它使用指针相等性。-member:的文档说明使用-isEqual:,这才是你实际想要测试的内容。 - Lily Ballard
感谢你们大家...你们的答案和评论真的帮了我很多。点赞给你们所有人... - Asad Khan
@Kevin,-containsObject:和-member:一样寻找成员。唯一的区别是返回BOOL与对象本身。 - Firoze Lafeer
@Kevin,我使用了alloc/init,因为我不希望该集合一直停留在自动释放池中。实际上,代码只需要在循环执行时该集合存在,因此这种方式可以尽早地将其从内存中释放。 - Jacob Relkin

7

我知道这是一个老问题,但这里有另一种可能性,具体取决于您的需求。

苹果公司确实提供了一种方法-键值编码集合运算符

对象运算符允许您对集合进行操作。在这种情况下,您需要:

@distinctUnionOfObjects

@distinctUnionOfObjects运算符返回一个数组,其中包含右侧运算符后面指定的属性中不同的对象。

NSArray *distinctArray = [arrayWithDuplicates valueForKeyPath:@"@distinctUnionOfObjects.name"];

然而,在您的情况下,您想要整个对象。因此,您需要执行两个操作: 1)改用@distinctUnionOfArrays。例如,如果您从其他集合获得这些自定义对象,请使用@distinctUnionOfArray.myCollectionOfObjects 2)在这些对象上实现isEqual:以返回它们的.name是否相等。


2

我可能因此会受到批评...

你可以将数组转换为字典。不确定这有多有效率,这取决于实现和比较调用,但它确实使用了哈希映射。

//Get unique entries
NSArray *myArray = @[@"Hello", @"World", @"Hello"];
NSDictionary *uniq = [NSDictionary dictionaryWithObjects:myArray forKeys:myArray];
NSLog(@"%@", uniq.allKeys);

注意,这可能会改变你的数组顺序。


呵呵呵,我喜欢这个! - Albert Renshaw
编辑:我刚刚花了15分钟修复我的代码中的一个错误。使用这个NSDictionary方法将不会保持你的数组的顺序。我的所有项目都被打乱了。这就是我使用hacky wordk-arounds的后果,其中OP前缀为“我将因此受到指责...”哈哈 - Albert Renshaw

1

这是一行非常简单的代码

NSArray *duplicateList = ... 

如果您不关心元素顺序,则为无序。
NSArray *withoutDUP1 = [[NSSet setWithArray:duplicateList] allObjects];

保持元素顺序有序。
NSArray *withoutDUP2 = [[NSOrderedSet orderedSetWithArray:duplicateList] array];

2
但是你的代码如何基于字段“名称”进行过滤? - Satyam

1
如果你担心顺序
NSArray * newArray =
        [[NSOrderedSet orderedSetWithArray:oldArray] array]; **// iOS 5.0 and later** 

1
如果您希望自定义的NSObject子类在名称相等时被视为相等,则可以实现isEqual:和hash。这将允许您将对象添加到NSSet / NSMutableSet(不同对象的集合)中。
然后,您可以使用NSSet的sortedArrayUsingDescriptors:方法轻松创建排序的NSArray。
MikeAsh写了一篇关于实现自定义相等性的很好的文章:Friday Q&A 2010-06-18: Implementing Equality and Hashing

0

实现isEqual方法使你的对象可以进行比较:

@interface SomeObject (Equality)
@end

@implementation SomeObject (Equality)

- (BOOL)isEqual:(SomeObject*)other
{
    return self.hash == other.hash;
}

- (NSUInteger)hash
{
    return self.name;///your case
}

@end

使用方法:

- (NSArray*)distinctObjectsFromArray:(NSArray*)array
{
    return [array valueForKeyPath:@"@distinctUnionOfObjects.self"];
}

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