按二维排序 NSArray - Objective-C
Posted
技术标签:
【中文标题】按二维排序 NSArray - Objective-C【英文标题】:Sorting NSArray by a 2nd Dimension - Objective-C 【发布时间】:2017-11-10 18:01:18 【问题描述】:从历史上看,这个排序工厂能够按一维对 Invoice Item 对象数组进行排序。各种排序维度作为 SortItem 对象的属性存储在枚举“状态”中。这是有效的实现。
+(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;
您会注意到我在未使用的方法签名中添加了一个辅助排序参数。我的目标是允许按此次级维度排序,按此次级维度对具有相同主维度值的发票项目对象进行排序。
因此,例如,如果两个项目具有相同的 .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
【问题讨论】:
我不明白为什么它是“困难的”。为什么这不正是 NSSortDescriptor 想要解决的问题??? 没有意识到,马特。谢谢。哇,你真的写了关于这个主题的书。 为了更简洁,将该 switch 语句交换为一个单独的方法,该方法可以被调用两次以根据传入的状态返回描述符 我要做的是让排序描述符成为 SortItem 状态的一部分。 是的!可以进一步压缩,因为在您的return
语句中唯一不同的是键和升序布尔值。我可以想象一个由状态键控的字典......!
【参考方案1】:
使用NSArray
的-sortedArrayUsingDescriptors:
来解决这个问题。使用这种方法,您可以提供一个NSSortDescriptor
s 的数组,其中第二个将用作二级排序(如果有第三个,它将作为三级排序,等等)。
您可以使用-[NSSortDescriptor initWithKey:ascending:comparator:]
创建NSSortDescriptor
并传递您的比较器块。
【讨论】:
【参考方案2】:使用 NSSortDescriptor 构造排序,如下所述:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
编辑:
在您的情况下,由于您有一个名为 state
的 SortItem 属性,它确定了排序描述符,并且您可以根据需要构造 state
类型,因此我建议您将排序描述符作为陈述自己。这样,当需要排序时,就没有工作要做了:状态本身提供了您将使用的排序描述符,您可以摆脱SortInvoiceItems
中的开关。一般来说,开关可以被视为一种不良气味,表明您没有正确设计对象类型;您应该让实际对象知道该做什么,而不是打开某些东西。
【讨论】:
甚至 KVC 也不是必需的,因为NSSortDescriptor
可以使用比较器块进行初始化,就像 OP 在问题中已有的那样。以上是关于按二维排序 NSArray - Objective-C的主要内容,如果未能解决你的问题,请参考以下文章
在 Objective-c 中访问二维 NSArray 中的值
在Objective-C中按一个属性对自定义对象的NSSet进行排序