Sort NSmutablearray with two special keys

南楼画角 提交于 2020-03-06 09:38:59

问题


I have a tableview, its header is stored in a mutablearray, the array looks like

(2005 fall, 2005 spring, 2007 summer...)

When I output the tableview, I want the header in time ascending displayed.

2005 spring
2005 fall
2007 summer

I used the code here:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    [self.sectionKeys sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
    NSString *key = [self.sectionKeys objectAtIndex:section];
    return key;
}

It works fine with year. However, fall comes before spring and summer because of alphabetreason , what to do to fix it please?


回答1:


So you have an array with section keys. But the sections are not in order of the array, they need to be sorted. You will notice that cellForRowAtIndexPath: needs the exact same information. So sorting in this place is wrong.

What I do to handle this: I have a property "unsortedSectionKeys" and a property "sortedSectionKeys". sortedSectionKeys has a getter that checks for nil and stores a sorted copy of unsortedSectionKeys if it is nil. And whenever unsortedSectionKeys changes, you just set sortedSectionKeys to nil. (That solves at least some problems).

For your sorting, you need to write proper code. Use (void)sortUsingComparator:(NSComparator)cmptr to sort a mutable, or - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr to get a sorted copy of an array.

Example:

[self.sectionKeys sortArrayUsingComparator:^NSComparisonResult(NSString* obj1, NSString* obj2) {
    NSInteger year1 = obj1.integerValue;
    NSInteger year2 = obj2.integerValue;
    if (year1 < year2) return NSOrderedAscending;
    if (year1 > year2) return NSOrderedDescending;

    NSInteger season1 = 0;
    if ([obj1 rangeOfString:@"spring" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season1 = 1;
    if ([obj1 rangeOfString:@"summer" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season1 = 2;
    if ([obj1 rangeOfString:@"fall" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season1 = 3;
    if ([obj1 rangeOfString:@"winter" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season1 = 4;

    NSInteger season2 = 0;
    if ([obj2 rangeOfString:@"spring" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season2 = 1;
    if ([obj2 rangeOfString:@"summer" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season2 = 2;
    if ([obj2 rangeOfString:@"fall" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season2 = 3;
    if ([obj2 rangeOfString:@"winter" options:NSCaseInsensitiveSearch].location != NSNotFound)
        season2 = 4;

    if (season1 < season2) return NSOrderedAscending;
    if (season1 > season2) return NSOrderedDescending;
    return NSOrderedSame;
}];

Your decision if winter is the first or last season in the year, since usually it's December to February.




回答2:


Use a custom comparator to get a custom sort order:

NSMutableArray *array = [@[ @"2005 fall", @"2005 spring", @"2007 summer" ] mutableCopy];

NSArray *seasons = @[ @"spring", @"summer", @"fall", @"winter" ];

[array sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
    NSArray *parts1 = [str1 componentsSeparatedByString:@" "];
    NSArray *parts2 = [str1 componentsSeparatedByString:@" "];

    NSString *year1 = parts1[0];
    NSString *year2 = parts2[0];
    NSComparisonResult yearRes = [year1 compare:year2 options:NSNumericSearch];
    if (yearRes == NSOrderedSame) {
        NSString *season1 = parts1[1];
        NSString *season2 = parts2[1];

        NSUInteger index1 = [seasons indexOfObject:season1];
        NSUInteger index2 = [seasons indexOfObject:season2];

        if (index1 < index2) {
            return NSOrderedAscending;
        } else if (index1 > index2) {
            return NSOrderedDescending;
        } else {
            return NSOrderedSame;
        }
    } else {
        return yearRes;
    }
}];

Note - I might have the NSOrderedAscending and NSOrderedDescending backwards. Swap them if the sort of the seasons in the same year come out in the reverse order.




回答3:


You need a lookup mechanism to define the ordering of the seasons

NSArray *seasons = @[@"spring", @"summer", @"fall", @"winter"];

NSArray *strings = @[@"2005 fall",@"2007 spring",  @"2005 spring", @"2007 winter", @"2005 winter"];
strings = [strings sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
    NSArray *string1Comps = [obj1 componentsSeparatedByString:@" "];
    NSArray *string2Comps = [obj2 componentsSeparatedByString:@" "];

    NSComparisonResult compareYearResult = [@([string1Comps[0] integerValue]) compare:@([string2Comps[0] integerValue]) ];
    if (compareYearResult == NSOrderedSame) {
        return [@([seasons indexOfObject:string1Comps[1]]) compare:@([seasons indexOfObject:string2Comps[1]])];
    }
    return compareYearResult;
}];

result

(
    2005 spring,
    2005 fall,
    2005 winter,
    2007 spring,
    2007 winter
)

Another look up mechanism could be a block

NSNumber* (^lookUpSeason)(NSString *) = ^(NSString *seasonname){
    static NSArray *seasons;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        seasons = @[@"spring", @"summer", @"fall", @"winter"];
    });
    return @([seasons indexOfObject:seasonname]);
};

This might look a bit cumbersome at first, but increases readability when used.

return [@([seasons indexOfObject:string1Comps[1]]) compare:@([seasons indexOfObject:string2Comps[1]])];

becomes

return [lookUpSeason(string1Comps[1]) compare:lookUpSeason(string2Comps[1])];

in both cases you could also give the lookup code into the comparator block, this will give you the opportunity to remove the same comparator with the lookup in other places.

like:

NSArray *strings = @[@"2005 fall", @"2007 spring",  @"2005 spring", @"2007 winter", @"2005 winter", @"2005 summer", @"2000 hhh"];

NSComparisonResult (^yearAndSeasonComparator)(id,id) = ^NSComparisonResult(NSString *obj1, NSString *obj2) {
    NSNumber* (^lookUpSeason)(NSString *) = ^(NSString *seasonname){
        static NSArray *seasons;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            seasons = @[@"spring", @"summer", @"fall", @"winter"];
        });
        return @([seasons indexOfObject:seasonname]);
    };

    NSArray *string1Comps = [obj1 componentsSeparatedByString:@" "];
    NSArray *string2Comps = [obj2 componentsSeparatedByString:@" "];

    NSComparisonResult compareYearResult = [@([string1Comps[0] integerValue]) compare:@([string2Comps[0] integerValue]) ];
    if (compareYearResult == NSOrderedSame) {
        return [lookUpSeason(string1Comps[1]) compare:lookUpSeason(string2Comps[1])];
    }
    return compareYearResult;
};

strings = [strings sortedArrayUsingComparator:yearAndSeasonComparator];

The block assigned to yearAndSeasonComparator could now be reused in other places that would sort similar strings.



来源:https://stackoverflow.com/questions/29851431/sort-nsmutablearray-with-two-special-keys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!