我有一个包含Order对象列表的NSArray,一个Order对象有三个属性(id,typeID和description),我想根据typeID过滤我的数组以排除重复项。重复项由typeID确定,例如,如果有2个类型为ID = 7的项目,那么我想选择具有最大ID的订单,因此在这种情况下它将是=> ID = 2。
我的带有Order对象的src数组:
Item 1: id=1, typeID=7, description="some text 1"
Item 2: id=2, typeID=7, description="some text 2"
Item 3: id=3, typeID=5, description="some text 3"
Item 4: id=4, typeID=5, description="some text 4"
Item 5: id=5, typeID=8, description="some text 5"
应用过滤器后,我返回的数组应如下所示:
Item 2: id=2, typeID=7, description="some text 2"
Item 4: id=4, typeID=5, description="some text 4"
Item 5: id=5, typeID=8, description="some text 5"
有人可以建议这样做的最佳方法,谢谢。
答案 0 :(得分:3)
方法一:
- (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
我想的是:
__block NSMutableSet *uniqueTypeIDs = [NSMutableSet set];
NSIndexSet *set = [myArrayOfObjects indexesOfObjectsPassingTest:^BOOL(id object, NSUInteger idx, BOOL *stop) {
if([uniqueTypeIDs containsObject:[NSNumber numberWithInt:object.typeID]]) {
return NO;
} else {
[uniqueTypeIDs addObject:[NSNumber numberWithInt:object.typeID]];
return YES;
}
}];
您的typeID是否需要转换为NSNumber?你决定。 返回的NSIndexSet将包含通过测试的所有对象的索引。然后,您可以对这些对象执行操作,或从阵列中删除它们。
方法二:
或者使用NSSet。如果您的对象非常独特,那么将数组转换为一个集合,然后再转换为数组 - 这是丢失重复对象的最简单方法。
NSSet *set = [NSSet setWithArray:array];
制作由唯一对象组成的集合
[set allObjects];
为您提供集合
中所有对象的数组方法三:
另一种方法是使用类型ID作为键的NSMutableDictionary;迭代数组,并使用typeID(转换为NSNumber)作为存储索引的键。如果您发现字典中已存在密钥,请不要再次添加。结果是一个字典,其中包含原始数组中唯一对象的索引。
答案 1 :(得分:3)
首先,感谢大家的所有提示,这就是我能够解决问题的方法:
-( NSArray *) filterOutDuplicateOrder: (NSArray *)unFilteredArray
{
// First sort array by descending so I could capture the max id
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"itemID" ascending:NO];
NSArray *sortedDescArray = [unFilteredArray sortedArrayUsingDescriptors:[NSArray arrayWithObjects:descriptor,nil]];
// Filter out duplicates using typeID
NSMutableArray *filteredArrayOfObjects = [[NSMutableArray alloc] init];
for (Order *order in sortedDescArray)
{
if(!([[filteredArrayOfObjects valueForKeyPath:@"typeID"] containsObject:order.typeID]))
{
[filteredArrayOfObjects addObject:progressNote];
}
}
return resultArray;
}
答案 2 :(得分:1)
首先使用排序方法(可能生成单独的副本)以确保首先按typeID
排序,然后按 reverse 中的id
排序:< / p>
id=4, typeID=5, description="some text 4"
id=3, typeID=5, description="some text 3"
id=2, typeID=7, description="some text 2"
id=1, typeID=7, description="some text 1"
id=5, typeID=8, description="some text 5"
现在按顺序遍历生成的数组,随时跟踪typeID
。如果typeID
与上一个项目不同(或者这是第一个项目),则可以保证,这一个进入结果数组(加星标的项目是那些):
id=4, typeID=5, description="some text 4" *
id=3, typeID=5, description="some text 3"
id=2, typeID=7, description="some text 2" *
id=1, typeID=7, description="some text 1"
id=5, typeID=8, description="some text 5" *
答案 3 :(得分:0)
没有理由低估亚当的答案。另外,他给出的第一种方法可能会更加简洁。
__block NSMutableSet *uniqueTypeIDs = [NSMutableSet set];
NSMutableArray *myFilteredArrayOfObjects = [NSMutableArray new];
[myArrayOfObjects indexesOfObjectsPassingTest:^BOOL(id object, NSUInteger idx, BOOL *stop) {
if([uniqueTypeIDs containsObject:[NSNumber numberWithInt:object.typeID]]) {
return NO;
} else {
[uniqueTypeIDs addObject:[NSNumber numberWithInt:object.typeID]];
[myFilteredArrayOfObjects addObject:object];
return YES;
}
}];
编辑 - 甚至这可能是一种方式。 (虽然没试过。)
NSMutableArray *myFilteredArrayOfObjects = [NSMutableArray new];
[myArrayOfObjects indexesOfObjectsPassingTest:^BOOL(id object, NSUInteger idx, BOOL *stop) {
if([[myFilteredArrayOfObjects valueForKeyPath:@"typeID"] containsObject:object.typeID]) {
return NO;
} else {
[myFilteredArrayOfObjects addObject:object];
return YES;
}
}];
答案 4 :(得分:0)
当然,如果我们阅读&#34;过滤重复&#34;我们想到集合ans过滤操作。但在这种情况下,这将是毛茸茸的,因为副本并不是真的重复,NSSet不会让我们有机会决定更喜欢哪个项目。
我选择首先对项目的typeID进行细分,选择每个细分中的第一个对象,然后为它们的ID进行排序。
我使用这个Item
类:
@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
请注意,id
和description
属性名称相当糟糕。
我使用此代码创建项目列表:
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;
})];
}];
这应该是您以类似方式拥有的所有代码。或者你不需要它。
我使用typeID作为键创建一个字典。作为我添加和填充可变数组的值:
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];
}];
现在我对每个可变数组进行排序:
[itemsByType enumerateKeysAndObjectsUsingBlock:^(id key, NSMutableArray *items, BOOL *stop) {
[items sortUsingComparator:^NSComparisonResult(Item *item1, Item *item2) {
return item1.itemID < item2.itemID;
}];
}];
并将每个数组的每个第一个对象放到结果中:
NSMutableArray *resultArray = [@[] mutableCopy];
[[itemsByType allKeys] enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) {
[resultArray addObject:itemsByType[key][0]];
}];
现在我按itemID对结果进行排序
[resultArray sortUsingComparator:^NSComparisonResult(Item *item1, Item *item2){
return item1.itemID > item2.itemID;
}];
结果:
NSLog(@"%@", resultArray);
打印
(
"Item: 2, typeID: 7, description: some text 2",
"Item: 4, typeID: 5, description: some text 4",
"Item: 5, typeID: 8, description: some text 5"
)
我的测试程序的源代码:gist
替代方案可以是对typeID升序和itemID降序进行排序。然后循环项目并将每个第一项用于看不见的类型ID。对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;
}];
答案 5 :(得分:0)
我认为最有效的方法是使用NSDictionary
将对象存储为值,将属性值存储为键,并在将任何对象添加到字典之前检查是否存在,哪个是O(1 )操作,即整个过程将采取O(n)
这是代码
- (NSArray *)removeDuplicatesFromArray:(NSArray *)array onProperty:(NSString *)propertyName {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
for (int i=0; i<array.count; i++) {
NSManagedObject *currentItem = array[i];
NSString *propertyValue = [currentItem valueForKey:propertyName];
if ([dictionary valueForKey:propertyValue] == nil) {
[dictionary setValue:currentItem forKey:propertyValue];
}
}
NSArray *uniqueItems = [dictionary allValues];
return uniqueItems;
}
答案 6 :(得分:0)
我使用了一种使用NSSet
非重复力量的方法。
这是代码
我在这里使用了+
方法,因为您可以在任何共享类中使用此方法,并在任何所需的类中访问它。
+ (NSArray *)removeDuplicateEntriesFromArray:(NSArray *)array basedOnKey:(NSString *)key{
NSMutableArray *newArray = [NSMutableArray new];
//get array containing all the keys.
NSArray *keysArray = [array valueForKey:key];
//putting these keys into a set which will remove duplicate keys
NSSet *noDuplicateKeys = [[NSSet alloc]initWithArray:keysArray];
for (NSString *currentKey in noDuplicateKeys) {
//Now searching objects with all the keys available in the set and putting those objects into newArray.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@",key ,currentKey];
NSArray *allObjectsWithKey = [array filteredArrayUsingPredicate:predicate];
[newArray addObject:[allObjectsWithKey firstObject]];
}
return [newArray copy];
}