18

I store names as keys and scores as values into an NSDictionary for saving in NSUserDefaults. I then want to get back the keys sorted by score, but I can't seem to sort them numerically, only by string. The list of scores 100, 50, 300, 200, 500, for example, gives me 100, 200, 300, 50, 500.

Can this be done or do I need to go about this differently?

NSString *defaultNames[] = {@"Matt", @"Terry",@"Jessica",@"Sean",nil};
NSNumber *defaultScores[] = {@"600", @"500",@"100",@"50", nil};

NSDictionary *newScoreDict =  [NSDictionary dictionaryWithObjects:(id *)defaultScores forKeys:(id *)defaultNames count:7];

NSArray *currScores = [scoreDict keysSortedByValueUsingSelector:@selector(compare:)];
jscs
  • 63,694
  • 13
  • 151
  • 195
Matt
  • 189
  • 1
  • 1
  • 3
  • 3
    Why are you putting strings into your array of NSNumber pointers? – Carl Norum Oct 25 '09 at 06:07
  • that is an excellent question... hehe, i must have missed that – Matt Oct 25 '09 at 06:23
  • 1
    Why are you saying there are 7 objects in the arrays when there are really 4? – Dave DeLong Mar 04 '10 at 17:46
  • +1 for this very useful question. I used it in a different context. – Vignesh Feb 06 '12 at 14:02
  • http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/doc/uid/20000154-intValue – Azeem.Butt Oct 25 '09 at 04:42
  • I found that too, but how exactly does that help with keysSortedByValueUsingSelector:@selector(compare:) – Matt Oct 25 '09 at 04:45
  • If you don't see how converting strings into numbers might help you compare their values numerically then I dunno what to tell you. – Azeem.Butt Oct 25 '09 at 06:25
  • I want it to come out of the dictionary sorted so keys and values line up, it doesn't help me to compare it after the fact. – Matt Oct 25 '09 at 06:29
  • Um, I'm trying to learn, If I'm missing something obvious you could just say so instead of being a jackass – Matt Oct 25 '09 at 21:04

6 Answers6

7

how about using keysSortedByValueUsingSelector (NSDictionary)

Seems to be what you need as per the documentation in XCode

leukosaima
  • 504
  • 5
  • 6
  • 2
    @hmak: No. You must have been thinking of `keysSortedByValueUsingComparator:` The post mentions `keysSortedByValueUsingSelector:`, which has been available since 10.0 – user102008 Oct 09 '11 at 07:30
  • 1
    Should I mention that the question is for iOS not MacOS ? – Nicu Surdu Oct 23 '12 at 15:41
5
NSString *defaultNames[] = {@"Matt", @"Terry",@"Jessica",@"Sean",nil};
NSNumber *defaultScores[] = {@"600", @"500",@"100",@"50", nil};
NSDictionary *newScoreDict =  [NSDictionary dictionaryWithObjects:(id *)defaultScores forKeys:(id *)defaultNames count:7];
NSArray *currScores = [scoreDict keysSortedByValueUsingSelector:@selector(localizedStandardCompare:)];
Black Tiger
  • 1,201
  • 11
  • 12
  • I couldn't get this working since my values were NSNumbers. But I used this comparator and it worked: https://stackoverflow.com/a/9708772/2057171 – Albert Renshaw Oct 26 '17 at 21:21
3
@implementation NSString (numericComparison)

- (NSComparisonResult) floatCompare:(NSString *) other
{
    float myValue = [self floatValue];
    float otherValue = [other floatValue];
    if (myValue == otherValue) return NSOrderedSame;
    return (myValue < otherValue ? NSOrderedAscending : NSOrderedDescending);
}

- (NSComparisonResult) intCompare:(NSString *) other
{
    int myValue = [self intValue];
    int otherValue = [other intValue];
    if (myValue == otherValue) return NSOrderedSame;
    return (myValue < otherValue ? NSOrderedAscending : NSOrderedDescending);
}

@end

NSString *defaultNames[] = {@"Matt", @"Terry",@"Jessica",@"Sean",nil};
// NSNumber *defaultScores[] = {@"600", @"500",@"100",@"50", nil};

NSNumber *defaultScores[] = {                                                                   
            [NSNumber  numberWithInt:600],
            [NSNumber  numberWithInt:500],
            [NSNumber  numberWithInt:100],
            [NSNumber  numberWithInt:50],
            nil 
    };

NSDictionary *newScoreDict =  [NSDictionary dictionaryWithObjects:(id *)defaultScores forKeys:(id *)defaultNames count:4];

NSArray *currScores = [newScoreDict keysSortedByValueUsingSelector:@selector(intCompare:NotSureWhatGoesHere:)];

I am still confused with the previous line ?

Do I just use

//
NSArray *currScores = [newScoreDict keysSortedByValueUsingSelector:@selector(intCompare:other:)];
//

Is the array of numbers OK, or is there an easier way ?

Thank You Very Much...

2

-compare: is a string compare. Pass a different method for the comparison, e.g:

@implementation NSString (numericComparison)

- (NSComparisonResult) compareNumerically:(NSString *) other
{
float myValue = [self floatValue];
float otherValue = [other floatValue];
if (myValue == otherValue) return NSOrderedSame;
return (myValue < otherValue ? NSOrderedAscending : NSOrderedDescending);
}

@end

In your specific case, you could use -intValue instead.

NSResponder
  • 16,861
  • 7
  • 32
  • 46
  • Trying to write code similar to this but does not take in an NSString, instead takes in an int. What does [self floatValue]; point to? – prince Jun 26 '12 at 20:51
0

Not sure it would help, but you can also save an NSArray in a plist; unlike an NSDictionary (which returns keys in essentially random order), you get them back as you put them in.

David Dunham
  • 8,139
  • 3
  • 28
  • 41
0

I think simplest way of this question is using comparator..

NSString *defaultNames[] = {@"Matt", @"Terry",@"Jessica",@"Sean",nil};
NSNumber *defaultScores[] = {@(600), @(500),@(400),@(50), nil};
NSDictionary *newScoreDict =  [NSDictionary dictionaryWithObjects:defaultNames       forKeys:defaultScores count:4];
NSArray *currScores = [newScoreDict keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    if ([obj1 integerValue] > [obj2 integerValue]) {
        return NSOrderedAscending;
    }else{
        return NSOrderedDescending;
    }}];

for (NSString *string in currScores) {
    NSLog(@"%@",string);
}

try this.. I noticed that i couldnt reach value with using NSNumber object so if you want to reach object value than i solved with changing your NSNumber scores to NSString and convert them to numbers while ordering. You can use like below..

NSString *defaultNames[] = {@"Matt", @"Terry",@"Jessica",@"Sean",nil};
NSString *defaultScores[] = {@"600", @"500",@"400",@"50", nil};
NSMutableDictionary *newScoreDict =  [NSMutableDictionary   dictionaryWithObjects:defaultScores  forKeys:defaultNames count:4];

NSArray *currScores = [newScoreDict keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *number1 = [formatter numberFromString:obj1];
NSNumber *number2 = [formatter numberFromString:obj2];
    if (number1.intValue > number2.intValue) {
        return NSOrderedDescending;
    }else{
        return NSOrderedAscending;
    }}];

for (NSString *name in currScores) {
    NSLog(@"key %@ value %@",name,[newScoreDict valueForKey:name]);
}

Hope it helps..

mert
  • 1,090
  • 13
  • 26