按二维排序 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: 来解决这个问题。使用这种方法,您可以提供一个NSSortDescriptors 的数组,其中第二个将用作二级排序(如果有第三个,它将作为三级排序,等等)。

您可以使用-[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进行排序

在 Objective-C 中订购和发布 NSArray

Swift:按字母顺序对数组进行排序

如何按数组计数对嵌套 NSArray 的 NSArray 进行排序?

如何在Objective-C中将二维整数数组对象添加到NSMutableArray?