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

Posted

技术标签:

【中文标题】在多个属性上对 NSArray 自定义对象进行排序【英文标题】:Sort NSArray custom object on multiple properties 【发布时间】:2017-08-14 12:52:19 【问题描述】:

我有一个具有多个属性的自定义对象。

一个属性是一个类型,例如一个枚举:A、B、C 等

这些也都有一个日期。

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

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

我想先放置所有类型为“C”的对象,然后按日期 DESC 对其余对象进行排序。

实现这一目标的最佳方法是什么?

我尝试添加多个 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];

例子

+-------------------------+
| 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 |
+-------------------------+

想要

+-------------------------+
| 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 |
+-------------------------+

我还可以决定我首先想要哪个 customType,所以我可能希望 B 位于顶部,然后其余的只是按日期?


将数组分成两组是否更容易,ALL C 然后其余的。对每个子组进行排序,然后将它们重新组合在一起?

然后我可以在下面的谓词中选择我想要的最高组:

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];

按日期排序 https://***.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);

过去,我按给定属性对数组进行分组,并对该单个属性使用自定义排序。

How to use Custom ordering on 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;
];

【问题讨论】:

【参考方案1】:

我喜欢sortedArrayUsingComparator:,我不认为你可以用其他方法做你想做的事。

sortedArrayUsingComparator: 给出了关于如何排序的非常大的操作。 其背后的逻辑是: 比较它们之间的两个对象(obj1obj2)。 你决定这两个的排序。 它将遍历整个数组,并通过两两比较每个对象,它将得到排序结果。 因此,在该块中,您必须明确明确定义为什么一个对象相对于另一个对象“优先”。 这里CustomType 是其中之一,优先于startDate。所以让我们按照这个顺序来做吧。

然后实现:

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]];
    
];

稍微重构一下:

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]];
    
];

注意NSOrderedAscendingNSOrderedDescending可能要颠倒过来,我不知道用哪个,我一直在测试。

我将两个块中的id 替换为您的CustomClass,因为您已经知道您拥有的对象类别。这避免了一行演员:CustomType type1 = [(CustomClass *)obj1 customType];

【讨论】:

我已经换掉了第二种方法的else,这很有效,谢谢:) 既然你想要ascending:NOobj1obj2 确实需要倒置。

以上是关于在多个属性上对 NSArray 自定义对象进行排序的主要内容,如果未能解决你的问题,请参考以下文章

根据对另一个 NSArray 字符串的排序,对自定义对象的 NSArray 进行排序

在Objective-C中按一个属性对自定义对象的NSSet进行排序

按对象方法的结果对 NSArray 进行排序

NSArray sortUsingFunction 自定义对象总是在最后

按属性对自定义对象的 ArrayList 进行排序

Java:通用的排序工具类,能够对自定义对象,针对不同的属性(字段),实现排序(正序倒序)