在多个属性上对 NSArray 自定义对象进行排序
Sort NSArray custom object on multiple properties
我有一个具有多个属性的自定义对象。
一个 属性 是一个类型,例如枚举:A、B、C 等
这些也都有日期。
@property (assign) CustomType customType;
@property (nonatomic, strong) NSDate *startDate;
...
typedef NS_ENUM(NSInteger, CustomType) {
A,
B,
C
};
我想先放置类型为 'C' 的所有对象,然后按日期降序排列其余对象。
实现此目标的最佳方法是什么?
我试过添加多个 sortDescriptors,但我只希望 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 位于顶部,然后其余的只是按日期?
将数组分成两组,所有 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];
按日期排序
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;
}];
我喜欢sortedArrayUsingComparator:
,而且我不认为你可以用其他方法做你想做的事。
sortedArrayUsingComparator:
给出了关于如何排序的非常大的操作。
其背后的逻辑是:
比较它们之间的两个对象(obj1
和 obj2
)。
您根据这两个决定排序。
它将遍历整个数组,并通过逐个比较每个对象,得到排序后的结果。
因此,在该块中,您必须明确说明为什么一个对象 "prioritized" 与另一个对象相对。
这里 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]];
}
}];
注意NSOrderedAscending
和NSOrderedDescending
可能要反过来,一直不知道用哪个,一直在测试
我将两个块中的 id
替换为您的 CustomClass
,因为您已经知道您拥有的对象的 class。这避免了一行转换:CustomType type1 = [(CustomClass *)obj1 customType];
我有一个具有多个属性的自定义对象。
一个 属性 是一个类型,例如枚举:A、B、C 等
这些也都有日期。
@property (assign) CustomType customType;
@property (nonatomic, strong) NSDate *startDate;
...
typedef NS_ENUM(NSInteger, CustomType) {
A,
B,
C
};
我想先放置类型为 'C' 的所有对象,然后按日期降序排列其余对象。
实现此目标的最佳方法是什么?
我试过添加多个 sortDescriptors,但我只希望 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 位于顶部,然后其余的只是按日期?
将数组分成两组,所有 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];
按日期排序
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;
}];
我喜欢sortedArrayUsingComparator:
,而且我不认为你可以用其他方法做你想做的事。
sortedArrayUsingComparator:
给出了关于如何排序的非常大的操作。
其背后的逻辑是:
比较它们之间的两个对象(obj1
和 obj2
)。
您根据这两个决定排序。
它将遍历整个数组,并通过逐个比较每个对象,得到排序后的结果。
因此,在该块中,您必须明确说明为什么一个对象 "prioritized" 与另一个对象相对。
这里 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]];
}
}];
注意NSOrderedAscending
和NSOrderedDescending
可能要反过来,一直不知道用哪个,一直在测试
我将两个块中的 id
替换为您的 CustomClass
,因为您已经知道您拥有的对象的 class。这避免了一行转换:CustomType type1 = [(CustomClass *)obj1 customType];