views:

64

answers:

2

Hi all,

I'm looking for a nice clean routine for turning an NSArray containing NSNumbers (integers) into a nice English-readable string. For example, I want to change this:

[NSArray arrayWithObjects:[NSNumber numberWithInt:5],
                          [NSNumber numberWithInt:7],
                          [NSNumber numberWithInt:12],
                          [NSNumber numberWithInt:33], nil];

into this:

5, 7, 12 and 13.

Is there any nice way of doing this without hideous if statement logic? I'm sure it can be done with a regular expression, but is that possible in pre-iOS 4 code?

Thanks! :)

:-Joe

+2  A: 
NSArray *numbers = [NSArray arrayWithObjects:[NSNumber numberWithInt:5],
                          [NSNumber numberWithInt:7],
                          [NSNumber numberWithInt:12],
                          [NSNumber numberWithInt:33], nil];
NSArray *first = [numbers subarrayWithRange:NSMakeRange(0, [numbers count]-1)];
NSString *joined = [[first componentsJoinedByString:@", "] stringByAppendingFormat:(([first count] > 0) ? @"and %@" : @"%@"), [numbers lastObject]];
jtbandes
Don't know if it works but it looks like it should. Pretty awesome :D
Jorge Israel Peña
If this is for debugging purposes, don't forget about `[anArray description]`.
Justin
This results in *"and 5"* for an array containing only 5.
Georg Fritzsche
Thanks Georg, fixed.
jtbandes
I hate to be picky... but ;) I forgot to mention that it needs to work even if the Array is empty... This version crashes if it's empty :(
Joe
but I should be checking the array first anyway so it's okay :Pwill this version be any better than the one above? That's the question :)
Joe
@Joe It's much more readable, which in most cases would constitute "better". :)
jtbandes
+1  A: 

Look mom, no ifs!

NSString *seps[] = {@" and ",@"",@", ",nil,nil,@", "}, *o = @".", *sep = o;
for ( NSNumber *n in [arr reverseObjectEnumerator] ) {
   o = [NSString stringWithFormat:@"%@%@%@",n,sep = seps[[sep length]],o];
}

NSLog(@"out: %@",o);

output:

out: 5, 7, 12 and 33.

but why?

edit here is an understandable version with no "huge if/else construct"

NSString *out = @"";  // no numbers = empty string
NSString *sep = @"."; // the separator first used is actually the ending "."
for ( NSNumber *n in [arr reverseObjectEnumerator] )
{
    out = [NSString stringWithFormat:@"%@%@%@",n,sep,out];
    if ( [sep isEqualToString:@"."] ) // was the separator the ending "." ?
    {
        sep = @" and "; // if so, put in the last separator " and "
    } else {
        sep = @", ";    // otherwise, use separator ", "
    }
}

This will output strings like

0 elements: ""        (i.e. empty)
1 element:  "33."
2 elements: "12 and 33."
3 elements: "7, 12 and 33."
mvds
Genius! Thanks :)It's for a multitouch interface, where the user can select different items on the screen simultaneously. So I can write into a label in plain English, which numbers are being selected.Congratulations, you win... My admiration! ;-)
Joe
*don't you ever, ever put this in a real project* ;-)
mvds
why is it bad? :-.
Joe
because I made it with the purpose of being hard to understand. What I really meant to say is why no "if"? This is the typical case for an if statement; for **if** you're adding the last element, you take separator `" and "` and **else** you take separator `", "`. I think any other way will be harder to understand on the long run.
mvds
Yes I can appreciate that. I guess I meant I didn't want HUUUGE loads of statement logic if..then...else...if...then etc if I could avoid it! :)
Joe
@Joe: I added a version which you must be able to understand, and has only *one* `if`.
mvds