Can Objective-C switch on NSString?

Objective CSwitch Statement

Objective C Problem Overview


Is there a more intelligent way to rewrite this?

if ([cardName isEqualToString:@"Six"]) {
    [self setValue:6];
} else if ([cardName isEqualToString:@"Seven"]) {
    [self setValue:7];
} else if ([cardName isEqualToString:@"Eight"]) {
    [self setValue:8];
} else if ([cardName isEqualToString:@"Nine"]) {
    [self setValue:9];
} 

Objective C Solutions


Solution 1 - Objective C

Unfortunately they cannot. This is one of the best and most sought after utilizations of switch statements, so hopefully they hop on the (now) Java (and others) bandwagon!

If you are doing card names, perhaps assign each card object an integer value and switch on that. Or perhaps an enum, which is considered as a number and can therefore be switched upon.

e.g.

typedef enum{
  Ace, Two, Three, Four, Five ... Jack, Queen, King

} CardType;

Done this way, Ace would be be equal to case 0, Two as case 1, etc.

Solution 2 - Objective C

You could set up a dictionary of blocks, like this:

NSString *lookup = @"Hearts"; // The value you want to switch on

typedef void (^CaseBlock)();

// Squint and this looks like a proper switch!
NSDictionary *d = @{
    @"Diamonds": 
    ^{ 
        NSLog(@"Riches!"); 
    },
    @"Hearts":
    ^{ 
        self.hearts++;
        NSLog(@"Hearts!"); 
    },
    @"Clubs":
    ^{ 
        NSLog(@"Late night coding > late night dancing"); 
    },
    @"Spades":
    ^{ 
        NSLog(@"I'm digging it"); 
    }
};

((CaseBlock)d[lookup])(); // invoke the correct block of code

To have a 'default' section, replace the last line with:

CaseBlock c = d[lookup];
if (c) c(); else { NSLog(@"Joker"); }

Hopefully Apple will teach 'switch' a few new tricks.

Solution 3 - Objective C

For me, a nice easy way:

NSString *theString = @"item3";   // The one we want to switch on
NSArray *items = @[@"item1", @"item2", @"item3"];
int item = [items indexOfObject:theString];
switch (item) {
    case 0:
       // Item 1
       break;
    case 1:
       // Item 2
       break;
    case 2:
       // Item 3
       break;
    default:
       break;
}

Solution 4 - Objective C

Unfortunately, switch statements can only be used on primitive types. You do have a few options using collections, though.

Probably the best option would be to store each value as an entry in an NSDictionary.

NSDictionary *stringToNumber = [NSDictionary dictionaryWithObjectsAndKeys:
                                              [NSNumber numberWithInt:6],@"Six",
                                              [NSNumber numberWithInt:7],@"Seven",
                                              [NSNumber numberWithInt:8],@"Eight",
                                              [NSNumber numberWithInt:9],@"Nine",
                                              nil];
NSNumber *number = [stringToNumber objectForKey:cardName];
if(number) [self setValue:[number intValue]];

Solution 5 - Objective C

A bit late but for anyone in the future I was able to get this to work for me

#define CASE(str) if ([__s__ isEqualToString:(str)])
#define SWITCH(s) for (NSString *__s__ = (s); ; )
#define DEFAULT

Solution 6 - Objective C

Here is the more intelligent way to write that. It's to use an NSNumberFormatter in the "spell-out style":

NSString *cardName = ...;

NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setNumberStyle:NSNumberFormatterSpellOutStyle];
NSNumber *n = [nf numberFromString:[cardName lowercaseString]];
[self setValue:[n intValue]];
[nf release];

Note that the number formatter wants the string to be lowercased, so we have to do that ourselves before passing it in to the formatter.

Solution 7 - Objective C

There are other ways to do that, but switch isn't one of them.

If you only have a few strings, as in your example, the code you have is fine. If you have many cases, you could store the strings as keys in a dictionary and look up the corresponding value:

NSDictionary *cases = @{@"Six" : @6,
                        @"Seven" : @7,
                        //...
                       };
    
NSNumber *value = [cases objectForKey:cardName];
if (value != nil) {
    [self setValue:[value intValue]];
}

Solution 8 - Objective C

BY FAR.. [my FAVORITE "ObjC Add-On" is ObjectMatcher][1]

objswitch(someObject)
    objcase(@"one") { // Nesting works.
        objswitch(@"b")
            objcase(@"a") printf("one/a");
            objcase(@"b") printf("one/b");
            endswitch // Any code can go here, including break/continue/return.
    }
    objcase(@"two") printf("It's TWO.");  // Can omit braces.
    objcase(@"three",     // Can have multiple values in one case.
        nil,              // nil can be a "case" value.
        [self self],      // "Case" values don't have to be constants.
        @"tres", @"trois") { printf("It's a THREE."); }
    defaultcase printf("None of the above."); // Optional default must be at end.
endswitch

AND it works with non-strings, TOO... in loops, even!

for (id ifNumericWhatIsIt in @[@99, @0, @"shnitzel"])
    objswitch(ifNumericWhatIsIt)
        objkind(NSNumber)  printf("It's a NUMBER.... "); 
        objswitch([ifNumericWhatIsIt stringValue])
            objcase(@"3")   printf("It's THREE.\n"); 
            objcase(@"99")  printf("It's NINETY-NINE.\n"); 
            defaultcase     printf("some other Number.\n");
       endswitch
    defaultcase printf("It's something else entirely.\n");
endswitch

It's a NUMBER.... It's NINETY-NINE.
It's a NUMBER.... some other Number.
It's something else entirely.

Best of all, there are SO few {...}'s, :'s, and ()'s [1]: https://github.com/mralexgray/ObjectMatcher.git

Solution 9 - Objective C

Objective-c is no different from c in this aspect, it can only switch on what c can (and the preproc def's like NSInteger, NSUInteger, since they ultimately are just typedef'd to an integral type).

Wikipedia:

c syntax:

> The switch statement causes control to be transferred to one of several statements depending on the value of an expression, which must have integral type.

Integral Types:

> In computer science, an integer is a datum of integral data type, a > data type which represents some finite subset of the mathematical > integers. Integral data types may be of different sizes and may or may > not be allowed to contain negative values.

Solution 10 - Objective C

I'm kind of late to the party, but to answer the question as stated, there's a more intelligent way:

NSInteger index = [@[@"Six", @"Seven", @"Eight", @"Nine"] indexOfObject:cardName];
if (index != NSNotFound) [self setValue: index + 6];

Note that indexOfObject will look for the match using isEqual:, exactly as in the question.

Solution 11 - Objective C

Building on @Graham Perks idea posted earlier, designed a simple class to make switching on strings fairly simple and clean.

@interface Switcher : NSObject

+ (void)switchOnString:(NSString *)tString
                 using:(NSDictionary<NSString *, CaseBlock> *)tCases
           withDefault:(CaseBlock)tDefaultBlock;

@end

@implementation Switcher

+ (void)switchOnString:(NSString *)tString
                 using:(NSDictionary<NSString *, CaseBlock> *)tCases
           withDefault:(CaseBlock)tDefaultBlock
{
    CaseBlock blockToExecute = tCases[tString];
    if (blockToExecute) {
        blockToExecute();
    } else {
        tDefaultBlock();
    }
}

@end

You would use it like this:

[Switcher switchOnString:someString
                   using:@{
                               @"Spades":
                               ^{
                                   NSLog(@"Spades block");
                               },
                               @"Hearts":
                               ^{
                                   NSLog(@"Hearts block");
                               },
                               @"Clubs":
                               ^{
                                   NSLog(@"Clubs block");
                               },
                               @"Diamonds":
                               ^{
                                   NSLog(@"Diamonds block");
                               }
                           } withDefault:
                               ^{
                                   NSLog(@"Default block");
                               }
 ];

The correct block will execute according to the string.

Gist for this solution

Solution 12 - Objective C

You can use macros approach to achieve it:

#define CASE(str)  if ([__s__ isEqualToString:(str)]) 
#define SWITCH(s)  for (NSString *__s__ = (s); ; )
#define DEFAULT   

SWITCH (string) {
    CASE (@"TestString") {
        break;
    }
    CASE (@"YetAnotherString") {
        break;
    }
    CASE (@"Test") {
        break;
    }
    DEFAULT {
        break;
    }
 }

Solution 13 - Objective C

I can't Comment on cris's answer on @Cris answer but i would like to say that:

There is an LIMITATION for @cris's method:

typedef enum will not take alphanumeric values

typedef enum
{
  12Ace, 23Two, 23Three, 23Four, F22ive ... Jack, Queen, King

} CardType;

So here is another One:

Link Stack over flow Go to this user answer "user1717750"

Solution 14 - Objective C

typedef enum
{
    Six,
    Seven,
    Eight
} cardName;

- (void) switchcardName:(NSString *) param {
    switch([[cases objectForKey:param] intValue]) {
        case Six:
            NSLog(@"Six");
            break;
        case Seven:
            NSLog(@"Seven");
            break;
        case Eight:
            NSLog(@"Eight");
            break;
        default: 
            NSLog(@"Default");
            break;
    }
}

Enjoy Coding.....

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJames RaitsevView Question on Stackoverflow
Solution 1 - Objective CChrisView Answer on Stackoverflow
Solution 2 - Objective CGraham PerksView Answer on Stackoverflow
Solution 3 - Objective CsbonkoskyView Answer on Stackoverflow
Solution 4 - Objective CughoavgfhwView Answer on Stackoverflow
Solution 5 - Objective CNewyork167View Answer on Stackoverflow
Solution 6 - Objective CDave DeLongView Answer on Stackoverflow
Solution 7 - Objective CCalebView Answer on Stackoverflow
Solution 8 - Objective CAlex GrayView Answer on Stackoverflow
Solution 9 - Objective CchownView Answer on Stackoverflow
Solution 10 - Objective Cilya n.View Answer on Stackoverflow
Solution 11 - Objective CChuck KrutsingerView Answer on Stackoverflow
Solution 12 - Objective CArgusView Answer on Stackoverflow
Solution 13 - Objective CPuruView Answer on Stackoverflow
Solution 14 - Objective CEk SAD.View Answer on Stackoverflow