按二维对 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 中的开关。一般来说,开关可以看作是一种不好的气味,表明你没有正确设计你的对象类型;您应该让实际对象知道该做什么,而不是打开某些东西。