我有一个包含一些自定义对象的NSMutableArray。其中两个对象具有相同的属性,如标题和作者。我想删除重复的对象并离开另一个。
Asset *asset;
NSMutableArray *items = [[[NSMutableArray alloc] init] autorelease];
// First
asset = [[Asset alloc] init];
asset.title = @"Developer";
asset.author = @"John Smith";
[items addObject:asset];
[asset release];
// Second
asset = [[Asset alloc] init];
asset.title = @"Writer";
asset.author = @"Steve Johnson";
[items addObject:asset];
[asset release];
// Third
asset = [[Asset alloc] init];
asset.title = @"Developer";
asset.author = @"John Smith";
[items addObject:asset];
[asset release];
由于它们不是同一个对象,但只有重复的属性,我该如何删除副本?
答案 0 :(得分:13)
你可以创建一个HashSet,当你循环时,你可以将“title + author”连接集添加到HashSet(NSMutableSet)。当您到达每个项目时,如果HashSet包含您的密钥,请删除它或不复制(删除或创建没有重复的副本)。
这使它成为n阶(1循环)
这是NSMutableSet类:
使用代码进行编辑:
代码的核心是一个循环。
void print(NSMutableArray *assets)
{
for (Asset *asset in assets)
{
NSLog(@"%@/%@", [asset title], [asset author]);
}
}
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//
// Create the initial data set
//
Asset *asset;
NSMutableArray *items = [[[NSMutableArray alloc] init] autorelease];
// First
asset = [[Asset alloc] init];
asset.title = @"Developer";
asset.author = @"John Smith";
[items addObject:asset];
[asset release];
// Second
asset = [[Asset alloc] init];
asset.title = @"Writer";
asset.author = @"Steve Johnson";
[items addObject:asset];
[asset release];
// Third
asset = [[Asset alloc] init];
asset.title = @"Developer";
asset.author = @"John Smith";
[items addObject:asset];
[asset release];
NSLog(@"****Original****");
print(items);
//
// filter the data set in one pass
//
NSMutableSet *lookup = [[NSMutableSet alloc] init];
for (int index = 0; index < [items count]; index++)
{
Asset *curr = [items objectAtIndex:index];
NSString *identifier = [NSString stringWithFormat:@"%@/%@", [curr title], [curr author]];
// this is very fast constant time lookup in a hash table
if ([lookup containsObject:identifier])
{
NSLog(@"item already exists. removing: %@ at index %d", identifier, index);
[items removeObjectAtIndex:index];
}
else
{
NSLog(@"distinct item. keeping %@ at index %d", identifier, index);
[lookup addObject:identifier];
}
}
NSLog(@"****Filtered****");
print(items);
[pool drain];
return 0;
}
这是输出:
Craplet[11991:707] ****Original****
Craplet[11991:707] Developer/John Smith
Craplet[11991:707] Writer/Steve Johnson
Craplet[11991:707] Developer/John Smith
Craplet[11991:707] distinct item. keeping Developer/John Smith at index 0
Craplet[11991:707] distinct item. keeping Writer/Steve Johnson at index 1
Craplet[11991:707] item already exists. removing: Developer/John Smith at index 2
Craplet[11991:707] ****Filtered****
Craplet[11991:707] Developer/John Smith
Craplet[11991:707] Writer/Steve Johnson
答案 1 :(得分:4)
您可以使用NSSet
的唯一性从原始数组中获取不同的项目。如果您拥有Assest
的源代码,则需要覆盖isEqual:
类的hash
和Asset
方法。
@interface Asset : NSObject
@property(copy) NSString *title, *author;
@end
@implementation Asset
@synthesize title, author;
-(NSUInteger)hash
{
NSUInteger prime = 31;
NSUInteger result = 1;
result = prime * result + [self.title hash];
result = prime * result + [self.author hash];
return result;
}
-(BOOL)isEqual:(id)object
{
return [self.title isEqualToString:[object title]] &&
[self.author isEqualToString:[object author]];
}
- (void)dealloc {
[title release];
[author release];
[super dealloc];
}
@end
然后实施:
Asset *asset;
NSMutableArray *items = [[[NSMutableArray alloc] init] autorelease];
// First
asset = [[Asset alloc] init];
asset.title = @"Developer";
asset.author = @"John Smith";
[items addObject:asset];
[asset release];
// Second
asset = [[Asset alloc] init];
asset.title = @"Writer";
asset.author = @"Steve Johnson";
[items addObject:asset];
[asset release];
// Third
asset = [[Asset alloc] init];
asset.title = @"Developer";
asset.author = @"John Smith";
[items addObject:asset];
[asset release];
NSLog(@"Items: %@", items);
NSSet *distinctItems = [NSSet setWithArray:items];
NSLog(@"Distinct: %@", distinctItems);
如果你最后需要一个数组,你可以调用[distinctItems allObjects]
答案 2 :(得分:2)
首先,我会覆盖资产的isEqual:方法,如下所示:
-(BOOL)isEqual:(Asset *)otherAsset {
return [self.title isEqual:otherAsset.title] && [self.author isEqual:otherAsset.author];
}
然后,如果你想避免首先在数组中放置重复项:
NSUInteger idx = [items indexOfObject:asset]; // tests objects for equality using isEqual:
if (idx == NSNotFound) [items addObject:asset];
如果数组已包含重复项,那么找到它们的任何算法的运行时间都比线性更差,但我认为创建一个新数组并且只添加上面的唯一元素是最好的算法。像这样:
NSMutableArray *itemsWithUniqueElements = [NSMutableArray arrayWithCapacity:[items count]];
for (Asset *anAsset in items) {
if ([items indexOfObject:anAsset] == NSNotFound)
[itemsWithUniqueElements addObject:anAsset];
}
[items release];
items = [itemsWithUniqueElements retain];
在最坏的情况下(所有元素都是唯一的),迭代次数为:
1 + 2 + 3 + ... + n = n * (n+1) / 2
哪个仍然是O(n ^ 2),但比@Justin Meiners的算法略胜一筹。没有冒犯的意思! :)
答案 3 :(得分:0)
这是你可以做到的一种方式 :
NSMutableArray* toRemove = [NSMutableArray array];
for (Asset* asset1 in items)
{
for (Asset* asset2 in items)
{
if (asset1 != asset2)
{
if ([asset1.title isEqualToString:asset2.title] && [asset1.author isEqualToString:asset2.author])
{
[toRemove addObject:asset2];
}
}
}
}
for (Asset* deleted in toRemove)
{
[items removeObject:toRemove];
}
答案 4 :(得分:0)
如果您希望自定义NSObject子类的名称相等,则可以实现isEqual:
和hash
。这将允许您将对象添加到NSSet
/ NSMutableSet
(一组不同的对象)。
然后,您可以使用NSArray
的{{1}}方法轻松创建排序NSSet
。
MikeAsh写了一篇关于实现自定义平等的非常可靠的文章:Friday Q&A 2010-06-18: Implementing Equality and Hashing