Naturally if we read "filter duplicates" we think of sets ans filter operation. But this would be hairy in this case, as the duplicates aren't really duplicates and NSSet won't give us the opportunity to decide which item to prefer.
I choose to first segment the items for its typeID, pick the first object in every segment and than order them for its ids.
pre work
I use this Item class:
@interface Item : NSObject
@property NSInteger itemID;
@property NSInteger typeID;
@property(copy) NSString *itemDescription;
@end
@implementation Item
-(NSString *)description
{
return [NSString stringWithFormat:@"Item: %li, typeID: %li, description: %@", (long)self.itemID, (long)self.typeID, self.itemDescription];
}
@end
Note, that id and description are rather bad property names.
I use this code to create a list of items:
NSArray *data =@[ @{@"itemID": @1, @"typeID": @7, @"description": @"some text 1"},
@{@"itemID": @2, @"typeID": @7, @"description": @"some text 2"},
@{@"itemID": @3, @"typeID": @5, @"description": @"some text 3"},
@{@"itemID": @4, @"typeID": @5, @"description": @"some text 4"},
@{@"itemID": @5, @"typeID": @8, @"description": @"some text 5"}];
NSMutableArray *items = [@[ ] mutableCopy];
[data enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
[items addObject:({
Item *item = [[Item alloc] init];
item.itemID = [obj[@"itemID"] integerValue];
item.typeID = [obj[@"typeID"] integerValue];
item.itemDescription = obj[@"description"];
item;
})];
}];
This should be all code that you have in a similar way. or you don't need it.
the answer
I create a dictionary with the typeIDs as keys. as values I add and fill mutable arrays:
NSMutableDictionary *itemsByType = [@{} mutableCopy];
[items enumerateObjectsUsingBlock:^(Item *item, NSUInteger idx, BOOL *stop) {
id key = @(item.typeID);
if (![[itemsByType allKeys] containsObject:key]) {
itemsByType[key] = [@[] mutableCopy];
}
[itemsByType[key] addObject:item];
}];
Now I sort each of this mutable arrays:
[itemsByType enumerateKeysAndObjectsUsingBlock:^(id key, NSMutableArray *items, BOOL *stop) {
[items sortUsingComparator:^NSComparisonResult(Item *item1, Item *item2) {
return item1.itemID < item2.itemID;
}];
}];
and put every first object for each array to the results:
NSMutableArray *resultArray = [@[] mutableCopy];
[[itemsByType allKeys] enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) {
[resultArray addObject:itemsByType[key][0]];
}];
Now I sort the results by the itemID
[resultArray sortUsingComparator:^NSComparisonResult(Item *item1, Item *item2){
return item1.itemID > item2.itemID;
}];
The result:
NSLog(@"%@", resultArray);
prints
(
"Item: 2, typeID: 7, description: some text 2",
"Item: 4, typeID: 5, description: some text 4",
"Item: 5, typeID: 8, description: some text 5"
)
The source code of my test program: gist
an alternative could be sorting for typeID ascending and for itemID descending. Than loop the items and take each first item for an unseen type id. Sort the result for typeID.
[items sortUsingDescriptors:@[[[NSSortDescriptor alloc] initWithKey:@"typeID" ascending:YES],
[[NSSortDescriptor alloc] initWithKey:@"itemID" ascending:NO]
]];
NSInteger lastestTypeID = -1;
NSMutableArray *result = [@[] mutableCopy];
for (Item *item in items) {
if (item.typeID > lastestTypeID) {
lastestTypeID = item.typeID;
[result addObject:item];
}
}
[result sortUsingComparator:^NSComparisonResult(Item *obj1, Item *obj2) {
return obj1.itemID > obj2.itemID;
}];