views:

219

answers:

3

So I have an NSDictionary where the keys are years as NSString's and the value for each key is also an NSString which is sort of a description for the year. So for example, one key is "943 B.C.", another "1886". The problem I am encountering is that I want to sort them, naturally, in ascending order.

The thing is that the data source of these years is already in order, it's just that when I go ahead and call setValue:forKey the order is lost, naturally. I imagine figuring out a way to sort these NSString's might be a pain and instead I should look for a method of preserving the order at the insertion phase. What should I do? Should I instead make this an NSMutableArray in which every object is actually an NSDictionary consisting of the key being the year and the value being the description?

I guess I just answered my own question, but to avoid having wasted this time I'll leave this up in case anyone can recommend a better way of doing this.

Thanks!

EDIT: I went ahead with my own idea of NSMutableArray with NSDictionary entries to hold the key/value pairs. This is how I am accessing the information later on, hopefully I'm doing this correctly:

// parsedData is the NSMutableArray which holdes the NSDictionary entries   
for (id entry in parsedData) {
    NSString *year = [[entry allKeys] objectAtIndex:0];
    NSString *text = [entry objectForKey:year];

    NSLog(@"Year: %@, Text: %@", year, text);
}
+2  A: 

Maintain a NSMutableArray to store the keys in order, in addition to the NSDictionary which holds all key-value pairs.

Here is a similar question.

Iamamac
+1  A: 

You could either do it as an array of dictionaries, as you suggest, or as an array of strings where the strings are the keys to your original dictionary. The latter is probably a simpler way of going about it. NSDictionary does not, as I understand it, maintain any particular ordering of its keys, so attempting to sort the values there may be unwise.

Noah Witherspoon
A: 

I needed to solve a similar problem to sort strings of operating system names, such as "Ubuntu 10.04 (lucid)".

In my case, the string could have any value, so I sort by tokenizing and testing to see if a token is a number. I'm also accounting for a string like "8.04.2" being considered a number, so I have a nested level of tokenizing. Luckily, the nested loop is typically only one iteration.

This is from the upcoming OpenStack iPhone app.

    - (NSComparisonResult)compare:(ComputeModel *)aComputeModel {
        NSComparisonResult result = NSOrderedSame;
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        NSArray *tokensA = [self.name componentsSeparatedByString:@" "];
        NSArray *tokensB = [aComputeModel.name componentsSeparatedByString:@" "];

        for (int i = 0; (i < [tokensA count] || i < [tokensB count]) && result == NSOrderedSame; i++) {
            NSString *tokenA = [tokensA objectAtIndex:i];
            NSString *tokenB = [tokensB objectAtIndex:i];

            // problem: 8.04.2 is not a number, so we need to tokenize again on .

            NSArray *versionTokensA = [tokenA componentsSeparatedByString:@"."];
            NSArray *versionTokensB = [tokenB componentsSeparatedByString:@"."];

            for (int j = 0; (j < [versionTokensA count] || j < [versionTokensB count]) && result == NSOrderedSame; j++) {
                NSString *versionTokenA = [versionTokensA objectAtIndex:j];
                NSString *versionTokenB = [versionTokensB objectAtIndex:j];
                NSNumber *numberA = [formatter numberFromString:versionTokenA];
                NSNumber *numberB = [formatter numberFromString:versionTokenB];

                if (numberA && numberB) {
                    result = [numberA compare:numberB];
                } else {
                    result = [versionTokenA compare:versionTokenB];
                }
            }
        }
        [formatter release];
        return result;
    }
greenisus