按二维对 NSArray 进行排序 - Objective-C
Sorting NSArray by a 2nd Dimension - Objective-C
从历史上看,这个排序工厂已经能够按一维对一组发票项目对象进行排序。各种排序维度作为 SortItem 对象的 属性 存储在枚举 "state" 中。这是工作实施。
+(NSArray *)SortInvoiceItems:(NSArray *)items forSort:(SortItem*)sortItem forSecondarySort:(SortItem*)secondarySortItem {
NSArray * primary = [items sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
InvoiceItems *iiA = (InvoiceItems *)a;
InvoiceItems *iiB = (InvoiceItems *)b;
switch(sortItem.state) {
case DateAscending:
case DateDescending: {
return (sortItem.state == DateAscending) ? [iiA.transactionDate compare:iiB.transactionDate] : [iiB.transactionDate compare:iiA.transactionDate];
}
case SumDescending:
case SumAscending: {
return (sortItem.state == SumAscending) ? [iiA.netInvoiceAmount compare:iiB.netInvoiceAmount] : [iiB.netInvoiceAmount compare:iiA.netInvoiceAmount];
}
case UnitPriceDescending:
case UnitPriceAscending: {
return (sortItem.state == UnitPriceAscending) ? [iiA.uomNetAmt compare:iiB.uomNetAmt] : [iiB.uomNetAmt compare:iiA.uomNetAmt];
}
default:
return 0;
}
}];
return primary;
}
您会注意到我在未使用的方法签名中添加了一个 secondarySort 参数。我的objective是允许按这个次级维度排序,按这个次级维度对主维度有相同值的Invoice Item对象进行排序。
因此,如果两个项目具有相同的 .transactionDate,则这两个项目将另外按第二个维度排序,例如单价降序。
编辑:我让它在下面工作,有没有更简洁的方式来写这个?
+(NSArray *)SortInvoiceItems:(NSArray *)items forSort:(SortItem*)sortItem forSecondarySort:(SortItem*)secondarySortItem {
NSSortDescriptor *primaryDescriptor;
NSSortDescriptor *secondaryDescriptor;
switch(sortItem.state) {
case DateAscending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:YES];
break;
case DateDescending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:NO];
break;
case SumAscending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:YES];
break;
case SumDescending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:NO];
break;
case UnitPriceAscending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:YES];
break;
case UnitPriceDescending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:NO];
break;
default: NSLog(@"invalid sort item");
}
switch(secondarySortItem.state) {
case DateAscending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:YES];
break;
case DateDescending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:NO];
break;
case SumAscending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:YES];
break;
case SumDescending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:NO];
break;
case UnitPriceAscending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:YES];
break;
case UnitPriceDescending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:NO];
break;
default: NSLog(@"invalid sort item");
}
NSArray *sortDescriptors = @[primaryDescriptor, secondaryDescriptor];
return [items sortedArrayUsingDescriptors:sortDescriptors];
}
第二次编辑:重构为:
+(NSArray *)SortInvoiceItems:(NSArray *)items forPrimarySort:(SortItem*)primary forSecondarySort:(SortItem*)secondary {
NSSortDescriptor *primaryDescriptor = [self GetDescriptorForSortItem:primary];
NSSortDescriptor *secondaryDescriptor = [self GetDescriptorForSortItem:secondary];
NSArray *sortDescriptors = @[primaryDescriptor, secondaryDescriptor];
return [items sortedArrayUsingDescriptors:sortDescriptors];
}
+(NSSortDescriptor *)GetDescriptorForSortItem:(SortItem*)sortItem {
switch(sortItem.state) {
case DateAscending:
return [[NSSortDescriptor alloc] initWithKey:kTransactionDateString ascending:YES];
case DateDescending:
return [[NSSortDescriptor alloc] initWithKey:kTransactionDateString ascending:NO];
case SumAscending:
return [[NSSortDescriptor alloc] initWithKey:kNetInvoiceAmount ascending:YES];
case SumDescending:
return [[NSSortDescriptor alloc] initWithKey:kNetInvoiceAmount ascending:NO];
case UnitPriceAscending:
return [[NSSortDescriptor alloc] initWithKey:kUOMNetAmount ascending:YES];
case UnitPriceDescending:
return [[NSSortDescriptor alloc] initWithKey:kUOMNetAmount ascending:NO];
default:
return [[NSSortDescriptor alloc] initWithKey:kTransactionDateString ascending:NO]; // default to date descending
}
}
使用NSArray
的-sortedArrayUsingDescriptors:
来解决这个问题。使用此方法,您可以提供一个 NSSortDescriptor
数组,其中第二个将用作二级排序(如果有第三个,它将是三级排序等)。
您可以使用 -[NSSortDescriptor initWithKey:ascending:comparator:]
创建一个 NSSortDescriptor
并传递比较器块。
使用 NSSortDescriptor 构建排序,如此处解释:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
编辑:
在您的情况下,由于您有一个名为 state
的 SortItem 属性 来确定排序描述符,并且由于您有能力根据需要构造 state
类型,所以我建议您使排序描述符成为状态本身的一部分。这样,当需要排序时,没有工作要做:状态本身提供您将使用的排序描述符,您可以摆脱 SortInvoiceItems
中的开关。一般来说,开关可以看作是一种不好的气味,表明你没有正确设计你的对象类型;您应该让实际对象知道该做什么,而不是打开某些东西。
从历史上看,这个排序工厂已经能够按一维对一组发票项目对象进行排序。各种排序维度作为 SortItem 对象的 属性 存储在枚举 "state" 中。这是工作实施。
+(NSArray *)SortInvoiceItems:(NSArray *)items forSort:(SortItem*)sortItem forSecondarySort:(SortItem*)secondarySortItem {
NSArray * primary = [items sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
InvoiceItems *iiA = (InvoiceItems *)a;
InvoiceItems *iiB = (InvoiceItems *)b;
switch(sortItem.state) {
case DateAscending:
case DateDescending: {
return (sortItem.state == DateAscending) ? [iiA.transactionDate compare:iiB.transactionDate] : [iiB.transactionDate compare:iiA.transactionDate];
}
case SumDescending:
case SumAscending: {
return (sortItem.state == SumAscending) ? [iiA.netInvoiceAmount compare:iiB.netInvoiceAmount] : [iiB.netInvoiceAmount compare:iiA.netInvoiceAmount];
}
case UnitPriceDescending:
case UnitPriceAscending: {
return (sortItem.state == UnitPriceAscending) ? [iiA.uomNetAmt compare:iiB.uomNetAmt] : [iiB.uomNetAmt compare:iiA.uomNetAmt];
}
default:
return 0;
}
}];
return primary;
}
您会注意到我在未使用的方法签名中添加了一个 secondarySort 参数。我的objective是允许按这个次级维度排序,按这个次级维度对主维度有相同值的Invoice Item对象进行排序。
因此,如果两个项目具有相同的 .transactionDate,则这两个项目将另外按第二个维度排序,例如单价降序。
编辑:我让它在下面工作,有没有更简洁的方式来写这个?
+(NSArray *)SortInvoiceItems:(NSArray *)items forSort:(SortItem*)sortItem forSecondarySort:(SortItem*)secondarySortItem {
NSSortDescriptor *primaryDescriptor;
NSSortDescriptor *secondaryDescriptor;
switch(sortItem.state) {
case DateAscending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:YES];
break;
case DateDescending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:NO];
break;
case SumAscending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:YES];
break;
case SumDescending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:NO];
break;
case UnitPriceAscending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:YES];
break;
case UnitPriceDescending:
primaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:NO];
break;
default: NSLog(@"invalid sort item");
}
switch(secondarySortItem.state) {
case DateAscending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:YES];
break;
case DateDescending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"transactionDate" ascending:NO];
break;
case SumAscending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:YES];
break;
case SumDescending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"netInvoiceAmount" ascending:NO];
break;
case UnitPriceAscending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:YES];
break;
case UnitPriceDescending:
secondaryDescriptor = [[NSSortDescriptor alloc] initWithKey:@"uomNetAmt" ascending:NO];
break;
default: NSLog(@"invalid sort item");
}
NSArray *sortDescriptors = @[primaryDescriptor, secondaryDescriptor];
return [items sortedArrayUsingDescriptors:sortDescriptors];
}
第二次编辑:重构为:
+(NSArray *)SortInvoiceItems:(NSArray *)items forPrimarySort:(SortItem*)primary forSecondarySort:(SortItem*)secondary {
NSSortDescriptor *primaryDescriptor = [self GetDescriptorForSortItem:primary];
NSSortDescriptor *secondaryDescriptor = [self GetDescriptorForSortItem:secondary];
NSArray *sortDescriptors = @[primaryDescriptor, secondaryDescriptor];
return [items sortedArrayUsingDescriptors:sortDescriptors];
}
+(NSSortDescriptor *)GetDescriptorForSortItem:(SortItem*)sortItem {
switch(sortItem.state) {
case DateAscending:
return [[NSSortDescriptor alloc] initWithKey:kTransactionDateString ascending:YES];
case DateDescending:
return [[NSSortDescriptor alloc] initWithKey:kTransactionDateString ascending:NO];
case SumAscending:
return [[NSSortDescriptor alloc] initWithKey:kNetInvoiceAmount ascending:YES];
case SumDescending:
return [[NSSortDescriptor alloc] initWithKey:kNetInvoiceAmount ascending:NO];
case UnitPriceAscending:
return [[NSSortDescriptor alloc] initWithKey:kUOMNetAmount ascending:YES];
case UnitPriceDescending:
return [[NSSortDescriptor alloc] initWithKey:kUOMNetAmount ascending:NO];
default:
return [[NSSortDescriptor alloc] initWithKey:kTransactionDateString ascending:NO]; // default to date descending
}
}
使用NSArray
的-sortedArrayUsingDescriptors:
来解决这个问题。使用此方法,您可以提供一个 NSSortDescriptor
数组,其中第二个将用作二级排序(如果有第三个,它将是三级排序等)。
您可以使用 -[NSSortDescriptor initWithKey:ascending:comparator:]
创建一个 NSSortDescriptor
并传递比较器块。
使用 NSSortDescriptor 构建排序,如此处解释:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
编辑:
在您的情况下,由于您有一个名为 state
的 SortItem 属性 来确定排序描述符,并且由于您有能力根据需要构造 state
类型,所以我建议您使排序描述符成为状态本身的一部分。这样,当需要排序时,没有工作要做:状态本身提供您将使用的排序描述符,您可以摆脱 SortInvoiceItems
中的开关。一般来说,开关可以看作是一种不好的气味,表明你没有正确设计你的对象类型;您应该让实际对象知道该做什么,而不是打开某些东西。