简体   繁体   English

在多个属性上对NSArray自定义对象进行排序

[英]Sort NSArray custom object on multiple properties

I have a custom object with multiple properties. 我有一个具有多个属性的自定义对象。

One property is a Type, for example an enum: A, B, C etc 一个属性是类型,例如枚举:A,B,C等

These all have a Date too. 这些都有日期。

@property (assign) CustomType customType;
@property (nonatomic, strong) NSDate *startDate;
...

typedef NS_ENUM(NSInteger, CustomType) {
    A,
    B,
    C
};

I would like to put all objects of type 'C' first then order the rest by their Date DESC. 我想先放置所有类型为“ C”的对象,然后按其日期DESC排序其余的对象。

What is the best way to achieve this? 实现此目标的最佳方法是什么?

I've tried adding multiple sortDescriptors but I only want C objects at the top, then the rest ordered by date, this orders each grouping by date. 我尝试添加多个sortDescriptor,但是我只希望C对象位于顶部,然后其余按日期排序,这按日期对每个分组进行排序。

NSSortDescriptor *sortDescriptor1 =[[NSSortDescriptor alloc] initWithKey:@"customType" ascending:NO];
NSSortDescriptor *sortDescriptor2 =[[NSSortDescriptor alloc] initWithKey:@"startDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
NSArray *sortedObjects = [_items sortedArrayUsingDescriptors:sortDescriptors];

Example

+-------------------------+
| C | 2017-08-16 12:48:53 |
| C | 2017-08-15 12:46:53 |
| B | 2017-08-16 12:48:53 |
| B | 2017-08-14 12:43:53 |
| A | 2017-08-15 12:46:53 |
| A | 2017-08-14 12:31:53 |
+-------------------------+

Would like

+-------------------------+
| C | 2017-08-16 12:48:53 |
| C | 2017-08-15 12:46:53 |
| A | 2017-08-16 12:48:53 |
| B | 2017-08-15 12:46:53 |
| A | 2017-08-14 12:43:53 |
| B | 2017-08-14 12:31:53 |
+-------------------------+

Also can I decide which customType I would like first, so I might want B to be at the top, then the rest just by date? 我还可以决定先选择哪个customType,这样我可能希望B在顶部,然后其余按日期排列?


Would it be easier to split the array into two groups, of ALL C then the rest. 将数组分为两组,然后是ALL C,会更容易吗? Sort each sub group, then join them back together? 对每个子组进行排序,然后将它们重新组合在一起?

I can then choose which top group I want in the below predicate: 然后,我可以在以下谓词中选择所需的顶级组:

NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"customType == %ld", C];
NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"customType != %ld", C];
NSArray *top = [_items filteredArrayUsingPredicate:pred1];
NSArray *bottom = [_items filteredArrayUsingPredicate:pred2];

NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"startDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
NSArray *sortedTop = [top sortedArrayUsingDescriptors:sortDescriptors];
NSArray *sortedBottom = [bottom sortedArrayUsingDescriptors:sortDescriptors];

//NSArray *items = [NSArray arrayWithObjects:sortedTop, sortedBottom, nil];

NSArray *newArray = @[];
newArray = [newArray arrayByAddingObjectsFromArray:sortedTop];
newArray = [newArray arrayByAddingObjectsFromArray:sortedBottom];

_items = [newArray mutableCopy];

Sort by Date https://stackoverflow.com/a/6084944/2895831 按日期排序https://stackoverflow.com/a/6084944/2895831

NSLog(@"nodeEventArray == %@", nodeEventArray);
NSSortDescriptor *dateDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"startDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
NSArray *sortedEventArray = [nodeEventArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(@"sortedEventArray == %@", sortedEventArray);

In the past I've grouped an array by a given property and used a custom sort on that single property. 过去,我已经根据给定的属性对数组进行了分组,并对单个属性使用了自定义排序。

How to use Custom ordering on NSArray 如何在NSArray上使用自定义排序

@property (nonatomic, strong) NSString *level;

NSArray *levels = [array valueForKeyPath:@"@distinctUnionOfObjects.level"];

NSArray *levelsSortOrder = @[@"Beginner", @"Basic", @"Intermediate", @"Expert", @"Advanced", @"Developer", @"Seminar"];

NSArray *sortedArray = [levels sortedArrayUsingComparator: ^(id obj1, id obj2){
    NSUInteger index1 = [levelsSortOrder indexOfObject: obj1];
    NSUInteger index2 = [levelsSortOrder indexOfObject: obj2];
    NSComparisonResult ret = NSOrderedSame;
    if (index1 < index2)
    {
        ret = NSOrderedAscending;
    }
    else if (index1 > index2)
    {
        ret = NSOrderedDescending;
    }
    return ret;
}];

I like sortedArrayUsingComparator: , and I don't think you can do what you want with the others methods. 我喜欢sortedArrayUsingComparator:并且我认为您无法使用其他方法来完成您想做的事情。

sortedArrayUsingComparator: gives really large actions on how sort. sortedArrayUsingComparator:对排序方式进行非常大的操作。 The logic behind it is: Compare two objects between them ( obj1 and obj2 ). 其背后的逻辑是:比较它们之间的两个对象( obj1obj2 )。 You decide the sort on theses two. 您在这两个问题上决定排序。 It will iterate through the whole array, and by comparing each objects two by two it will have the sorted result. 它将遍历整个数组,并且通过将每个对象二乘二进行比较,将获得排序的结果。 So in the block, you have to explicit exactly what define why is an object "prioritized" against the other one. 因此,在该块中,您必须确切地明确定义为什么将一个对象相对于另一个对象“优先化”的原因。 Here CustomType is one of them, and is prioritized before startDate . 这里CustomType是其中之一,并且在startDate之前进行优先级排序。 So let's do it in this order. 因此,让我们按此顺序进行操作。

The implementation then: 然后执行:

NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator: ^(CustomClass *obj1, CustomClass2 *obj2){ 
    CustomType type1 = [obj1 customType]; 
    CustomType type2 = [obj2 customType]; 
    if (type1 == C && type2 == C)
    {
        return [[obj1 startDate] compare:[obj2 startDate]];
    }
    else if (type1 == C && type2 != C) 
    {
        return NSOrderedAscending;
    }
    else if (type1 != C && type2 == C)
    {
        return NSOrderedDescending;
    }
    else //if (type1 != C && type2 != C)
    {
        return [[obj1 startDate] compare:[obj2 startDate]];
    }
}];

With a little of refactorization: 进行一些重构:

NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator: ^(CustomClass *obj1, CustomClass2 *obj2){ 
    CustomType type1 = [obj1 customType]; 
    CustomType type2 = [obj2 customType]; 
    if (type1 == C && type2 != C) 
    {
        return NSOrderedAscending;
    }
    else if (type1 != C && type2 == C)
    {
        return NSOrderedDescending;
    }
    else //Either (type1 == C && type2 == C) OR (type1 != C && type2 != C)
    {
        //return [[obj1 startDate] compare:[obj2 startDate]];
        return [[obj2 startDate] compare:[obj1 startDate]];
    }
}];

Note that NSOrderedAscending and NSOrderedDescending may have to be reversed, I never know which one to use, I always test. 请注意, NSOrderedAscendingNSOrderedDescending可能必须逆转,我不知道要使用哪一个,我总是测试。

I replace id in both blocks with your CustomClass because you already know the class of objects you have. 我用您的CustomClass替换了两个块中的id ,因为您已经知道拥有的对象的类。 This avoids a line of cast: CustomType type1 = [(CustomClass *)obj1 customType]; 这样可以避免CustomType type1 = [(CustomClass *)obj1 customType];CustomType type1 = [(CustomClass *)obj1 customType];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM