Search code examples
objective-cioscocoa-touchsortingnsarray

Sorting an nsarray of strings not string based


So i have an array that i retrieve from a web service in no particular order

example:

0 => x large, 
1 => large, 
2 => XX large, 
3 => small,
4 => medium, 
5 => x small

I need to sort them: firstly based on specific - which could be reverse alphabetic:

small
medium
large

Secondly i need to sort them based on their 'x' counter parts:

x small
small
medium
large
x large
xx large

I know i can do this with brute force string matching but i would really like a suggestion on how to do this tidily, perhaps a regex or something more elegant?


Solution

  • Use NSComparator block syntax. Something like

    NSArray * sizes = [NSArray arrayWithObjects:  @"x small",@"small",@"medium",@"large",@"x large", nil];
    
    NSArray *sortedBySizes =[array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        if ([sizes indexOfObject:[obj1 size]]> [sizes indexOfObject:[obj2 size]])
            return (NSComparisonResult)NSOrderedAscending;
        if ([sizes indexOfObject:[obj1 size]]< [sizes indexOfObject:[obj2 size]])
            return (NSComparisonResult)NSOrderedDescending;
        return (NSComparisonResult)NSOrderedSame;
    }];
    

    In the second approach I added a mapping between the numbers send by the web server and the x-sizes. Now [obj size]; is suppose to return a NSNumber object.

    NSArray * sizesStrings = [NSArray arrayWithObjects:  @"x small",@"small",
                                                         @"medium",@"large",
                                                         @"x large",@"xx large", 
                                                         nil];
    NSArray * sizesNumbers = [NSArray arrayWithObjects:[NSNumber numberWithInt:5],
                                                       [NSNumber numberWithInt:3],
                                                       [NSNumber numberWithInt:4],
                                                       [NSNumber numberWithInt:1],
                                                       [NSNumber numberWithInt:0],
                                                       [NSNumber numberWithInt:2], 
                                                       nil];
    
    NSDictionary *sizes = [NSDictionary dictionaryWithObjects:sizesStrings 
                                                       forKeys:sizesNumbers];
    
    NSArray *sortedBySizes = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        NSString *sizeObj1String = [sizes objectForKey:[obj1 size]];
        NSString *sizeObj2String = [sizes objectForKey:[obj1 size]];
    
        int i1 = [sizesStrings indexOfObject:sizeObj1String];
        int i2 = [sizesStrings indexOfObject:sizeObj2String];
    
        if (i1 > i2)
            return (NSComparisonResult)NSOrderedAscending;
        if (i2 > i1)
            return (NSComparisonResult)NSOrderedDescending;
        return (NSComparisonResult)NSOrderedSame;
    }];
    

    The second task of the question — the grouping into small, medium, large — could be done like this:

    NSDictionary *groups = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSMutableArray array],[NSMutableArray array],[NSMutableArray array], nil] 
                                        forKeys:[NSArray arrayWithObjects:@"small",@"medium",@"large",nil]
                            ];
    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        int i = [[obj size] intValue];
        if (i == 5 || i == 3) 
            [[groups objectForKey:@"small"] addObject:obj];
        else if (i == 2 || i == 0 || i == 1)
            [[groups objectForKey:@"large"] addObject:obj];
        else
            [[groups objectForKey:@"medium"] addObject:obj];
    
    }];