jan
jan

Reputation: 893

NSNumberFormatter and 'th' 'st' 'nd' 'rd' (ordinal) number endings

Is there a way to use NSNumberFormatter to get the 'th' 'st' 'nd' 'rd' number endings?

EDIT:

Looks like it does not exist. Here's what I'm using.

+(NSString*)ordinalNumberFormat:(NSInteger)num{
    NSString *ending;

    int ones = num % 10;
    int tens = floor(num / 10);
    tens = tens % 10;
    if(tens == 1){
        ending = @"th";
    }else {
        switch (ones) {
            case 1:
                ending = @"st";
                break;
            case 2:
                ending = @"nd";
                break;
            case 3:
                ending = @"rd";
                break;
            default:
                ending = @"th";
                break;
        }
    }
    return [NSString stringWithFormat:@"%d%@", num, ending];
}

Adapted from nickf's answer here Is there an easy way in .NET to get "st", "nd", "rd" and "th" endings for numbers?

Upvotes: 40

Views: 27567

Answers (21)

Luchi Parejo Alcazar
Luchi Parejo Alcazar

Reputation: 151

public extension Int {
 var ordinalValue: String? {
    let formatter = NumberFormatter()
    formatter.numberStyle = .ordinal
    formatter.locale = Locale(identifier: "en_US")
    return formatter.string(from: NSNumber(value: self))
 }
}

Don't forget to add the Locale, if it is not added, it won't work. Usage:

let number = 2
let ordinalNumber = number.ordinalValue
print(ordinalNumber) //It will print 2nd

Upvotes: 1

Krunal Patel
Krunal Patel

Reputation: 1691

-- Swift 4/5 --

     let num = 1
     let formatter = NumberFormatter()
     formatter.numberStyle = .ordinal
     let day = formatter.string(from: NSNumber(value: num))
     
     print(day!)
     result - 1st

Upvotes: 14

Antonio Favata
Antonio Favata

Reputation: 1891

Here's a Swift solution that cycles through the user's preferred languages until it finds one with known rules (which are pretty easy to add) for ordinal numbers:

    extension Int {
        var localizedOrdinal: String {

            func ordinalSuffix(int: Int) -> String {
                for language in NSLocale.preferredLanguages() as [String] {

                switch language {
                    case let l where l.hasPrefix("it"):
                        return "°"
                    case let l where l.hasPrefix("en"):
                        switch int {
                        case let x where x != 11 && x % 10 == 1:
                            return "st"
                        case let x where x != 12 && x % 10 == 2:
                            return "nd"
                        case let x where x != 13 && x % 10 == 3:
                            return "rd"
                        default:
                            return "th"
                        }
                    default:
                        break
                    }
                }

                return ""
            }

            return "\(self)" + ordinalSuffix(self)
        }
    }

Upvotes: 1

ymutlu
ymutlu

Reputation: 6715

There is a simple solution for this

Swift

let formatter = NumberFormatter()
formatter.numberStyle = .ordinal
let first = formatter.string(from: 1) // 1st
let second = formatter.string(from: 2) // 2nd

Obj-c

NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
numberFormatter.numberStyle = NSNumberFormatterOrdinalStyle;
NSString* first = [numberFormatter stringFromNumber:@(1)]; // 1st
NSString* second = [numberFormatter stringFromNumber:@(2)]; // 2nd

Referance: hackingwithswift.com

Upvotes: 5

Folalu Timothy
Folalu Timothy

Reputation: 43

You can try this, Its well simplified.

function numberToOrdinal(n) {

  if (n==0) {
    return n;
   }
   var j = n % 10,
       k = n % 100;


   if (j == 1 && k != 11) {
       return n + "st";
   }
   if (j == 2 && k != 12) {
       return n + "nd";
   }
   if (j == 3 && k != 13) {
       return n + "rd";
   }
   return n + "th";
}

Upvotes: 0

Magoo
Magoo

Reputation: 2619

As of iOS 9

Swift 4

private var ordinalFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.numberStyle = .ordinal
    return formatter
}()

extension Int {
    var ordinal: String? {
        return ordinalFormatter.string(from: NSNumber(value: self))
    }
}

It's probably best to have the formatter outside the extension...

Upvotes: 27

Chris Nolet
Chris Nolet

Reputation: 9063

The correct way to do this from iOS 9 onwards, is:

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.numberStyle = NSNumberFormatterOrdinalStyle;

NSLog(@"%@", [numberFormatter stringFromNumber:@(1)]); // 1st
NSLog(@"%@", [numberFormatter stringFromNumber:@(2)]); // 2nd
NSLog(@"%@", [numberFormatter stringFromNumber:@(3)]); // 3rd, etc.

Alternatively:

NSLog(@"%@", [NSString localizedStringFromNumber:@(1)
                                     numberStyle:NSNumberFormatterOrdinalStyle]); // 1st

Upvotes: 45

Bhavesh Dhaduk
Bhavesh Dhaduk

Reputation: 1906

- (NSString *) formatOrdinalNumber:(NSInteger )number{
    NSString *result = nil;
    //0 remains just 0
    if (number == 0) {
        result = @"0";
    }

    //test for number between 3 and 21 as they follow a
    //slightly different rule and all end with th
    else if (number > 3 && number < 21)
    {
        result = [NSString stringWithFormat:@"%ld th",(long)number];
    }
    else {
        //return the last digit of the number e.g. 102 is 2
        NSInteger lastdigit = number % 10;
        switch (lastdigit)
        {
            case 1: result = [NSString stringWithFormat:@"%ld st",(long)number]; break;
            case 2: result = [NSString stringWithFormat:@"%ld nd",(long)number]; break;
            case 3: result = [NSString stringWithFormat:@"%ld rd",(long)number]; break;
            default: result = [NSString stringWithFormat:@"%ld th",(long)number];
        }
    }
    return result;
}

Upvotes: 0

Greg Lukosek
Greg Lukosek

Reputation: 1792

Other Swift solutions do not produce correct result and contain mistakes. I have translated CmKndy solution to Swift

extension Int {

    var ordinal: String {
        var suffix: String
        let ones: Int = self % 10
        let tens: Int = (self/10) % 10
        if tens == 1 {
            suffix = "th"
        } else if ones == 1 {
            suffix = "st"
        } else if ones == 2 {
            suffix = "nd"
        } else if ones == 3 {
            suffix = "rd"
        } else {
            suffix = "th"
        }
        return "\(self)\(suffix)"
    }

}

test result: 0th 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th 21st 22nd 23rd

Upvotes: 18

Luke Chase
Luke Chase

Reputation: 391

Here's a short Int extension for the English language that also accounts for and displays negative integers correctly:

extension Int {
    func ordinal() -> String {
        let suffix: String!
        // treat negative numbers as positive for suffix
        let number = (self < 0 ? self * -1 : self)

        switch number % 10 {
        case 0:
            suffix = self != 0 ? "th" : ""
        case 1:
            suffix = "st"
        case 2:
            suffix = "nd"
        case 3:
            suffix = "rd"
        default:
            suffix = "th"
        }

        return String(self) + suffix
    }
}

Upvotes: 0

maxkonovalov
maxkonovalov

Reputation: 3719

Here's a compact Swift extension suitable for all integer types:

extension IntegerType {
    func ordinalString() -> String {
        switch self % 10 {
        case 1...3 where 11...13 ~= self % 100: return "\(self)" + "th"
        case 1:    return "\(self)" + "st"
        case 2:    return "\(self)" + "nd"
        case 3:    return "\(self)" + "rd"
        default:   return "\(self)" + "th"
        }
    }
}

Example usage:

let numbers = (0...30).map { $0.ordinalString() }
print(numbers.joinWithSeparator(", "))

Output:

0th, 1st, 2nd, 3rd, 4th, 5th, 6th, 7th, 8th, 9th, 10th, 11th, 12th, 13th, 14th, 15th, 16th, 17th, 18th, 19th, 20th, 21st, 22nd, 23rd, 24th, 25th, 26th, 27th, 28th, 29th, 30th

Upvotes: 5

Eneko Alonso
Eneko Alonso

Reputation: 19662

A clean Swift version (for English only):

func ordinal(number: Int) -> String {
    if (11...13).contains(number % 100) {
        return "\(number)th"
    }
    switch number % 10 {
        case 1: return "\(number)st"
        case 2: return "\(number)nd"
        case 3: return "\(number)rd"
        default: return "\(number)th"
    }
}

Can be done as an extension for Int:

extension Int {

    func ordinal() -> String {
        return "\(self)\(ordinalSuffix())"
    }

    func ordinalSuffix() -> String {
        if (11...13).contains(self % 100) {
            return "th"
        }
        switch self % 10 {
            case 1: return "st"
            case 2: return "nd"
            case 3: return "rd"
            default: return "th"
        }
    }

}

Upvotes: 3

CmKndy
CmKndy

Reputation: 1341

This does the trick in one method (for English). Thanks nickf https://stackoverflow.com/a/69284/1208690 for original code in PHP, I just adapted it to objective C:-

-(NSString *) addSuffixToNumber:(int) number
{
    NSString *suffix;
    int ones = number % 10;
    int tens = (number/10) % 10;

    if (tens ==1) {
        suffix = @"th";
    } else if (ones ==1){
        suffix = @"st";
    } else if (ones ==2){
        suffix = @"nd";
    } else if (ones ==3){
        suffix = @"rd";
    } else {
        suffix = @"th";
    }

    NSString * completeAsString = [NSString stringWithFormat:@"%d%@", number, suffix];
    return completeAsString;
}

Upvotes: 22

Milap Kundalia
Milap Kundalia

Reputation: 1606

This will convert date to string and also add ordinal in the date. You can modify the date formatte by changing NSDateFormatter object

-(NSString*) getOrdinalDateString:(NSDate*)date
{
    NSString* string=@"";
    NSDateComponents *components = [[NSCalendar currentCalendar] components: NSCalendarUnitDay fromDate:date];

    if(components.day == 1 || components.day == 21 || components.day == 31)
        string = @"st";

    else if (components.day == 2 || components.day == 22)
        string = @"nd";

    else if (components.day == 3 || components.day == 23)
        string = @"rd";

    else
        string = @"th";


    NSDateFormatter *dateFormatte = [[NSDateFormatter alloc] init];
    [dateFormatte setFormatterBehavior:NSDateFormatterBehavior10_4];
    [dateFormatte setDateFormat:[NSString stringWithFormat:@"d'%@' MMMM yyyy",string]];

    NSString *dateString = [dateFormatte stringFromDate:date];
    return dateString;
}

Upvotes: 2

superarts.org
superarts.org

Reputation: 7238

It's quite simple in English. Here's a swift extension:

extension Int {
    var ordinal: String {
        get {
            var suffix = "th"
            switch self % 10 {
                case 1:
                    suffix = "st"
                case 2:
                    suffix = "nd"
                case 3:
                    suffix = "rd"
                default: ()
            }
            if 10 < (self % 100) && (self % 100) < 20 {
                suffix = "th"
            }
            return String(self) + suffix
        }
    }
}

Then call something like:

    cell.label_position.text = (path.row + 1).ordinal

Upvotes: 7

garafajon
garafajon

Reputation: 1458

Many of the solutions here don't handle higher numbers like 112. Here is a simple way to do it.

for(int i=0;i<1000;i++){
    int n = i;
    NSString* ordinal = @"th";
    if(n%10==1 && n%100!=11) ordinal = @"st";
    if(n%10==2 && n%100!=12) ordinal = @"nd";
    if(n%10==3 && n%100!=13) ordinal = @"rd";
    NSLog(@"You are the %d%@",i,ordinal);
}

Upvotes: 0

ahall
ahall

Reputation: 129

This was my brute force implementation to taking a NSString* representation of the date and returning the ordinal value. I feel it's much easier to read.

NSDictionary *ordinalDates = @{
    @"1": @"1st",
    @"2": @"2nd",
    @"3": @"3rd",
    @"4": @"4th",
    @"5": @"5th",
    @"6": @"6th",
    @"7": @"7th",
    @"8": @"8th",
    @"9": @"9th",
    @"10": @"10th",
    @"11": @"11th",
    @"12": @"12th",
    @"13": @"13th",
    @"14": @"14th",
    @"15": @"15th",
    @"16": @"16th",
    @"17": @"17th",
    @"18": @"18th",
    @"19": @"19th",
    @"20": @"20th",
    @"21": @"21st",
    @"22": @"22nd",
    @"23": @"23rd",
    @"24": @"24th",
    @"25": @"25th",
    @"26": @"26th",
    @"27": @"27th",
    @"28": @"28th",
    @"29": @"29th",
    @"30": @"30th",
    @"31": @"31st" };

Upvotes: -4

jpittman
jpittman

Reputation: 301

Just adding another implementation as a class method. I didn't see this question posted until after I implemented this from an example in php.

+ (NSString *)buildRankString:(NSNumber *)rank
{
    NSString *suffix = nil;
    int rankInt = [rank intValue];
    int ones = rankInt % 10;
    int tens = floor(rankInt / 10);
    tens = tens % 10;
    if (tens == 1) {
        suffix = @"th";
    } else {
        switch (ones) {
            case 1 : suffix = @"st"; break;
            case 2 : suffix = @"nd"; break;
            case 3 : suffix = @"rd"; break;
            default : suffix = @"th";
        }
    }
    NSString *rankString = [NSString stringWithFormat:@"%@%@", rank, suffix];
    return rankString;
}

Upvotes: 5

Abizern
Abizern

Reputation: 150565

Since the question asked for a number formatter, here's a rough one I made.

//
//  OrdinalNumberFormatter.h
//

#import <Foundation/Foundation.h>


@interface OrdinalNumberFormatter : NSNumberFormatter {

}

@end

and the implementation:

//
//  OrdinalNumberFormatter.m
//

#import "OrdinalNumberFormatter.h"


@implementation OrdinalNumberFormatter

- (BOOL)getObjectValue:(id *)anObject forString:(NSString *)string errorDescription:(NSString **)error {
    NSInteger integerNumber;
    NSScanner *scanner;
    BOOL isSuccessful = NO;
    NSCharacterSet *letters = [NSCharacterSet letterCharacterSet];

    scanner = [NSScanner scannerWithString:string];
    [scanner setCaseSensitive:NO];
    [scanner setCharactersToBeSkipped:letters];

    if ([scanner scanInteger:&integerNumber]){
        isSuccessful = YES;
        if (anObject) {
            *anObject = [NSNumber numberWithInteger:integerNumber];
        }
    } else {
        if (error) {
            *error = [NSString stringWithFormat:@"Unable to create number from %@", string];
        }
    }

    return isSuccessful;
}

- (NSString *)stringForObjectValue:(id)anObject {
    if (![anObject isKindOfClass:[NSNumber class]]) {
        return nil;
    }

    NSString *strRep = [anObject stringValue];
    NSString *lastDigit = [strRep substringFromIndex:([strRep length]-1)];

    NSString *ordinal;


    if ([strRep isEqualToString:@"11"] || [strRep isEqualToString:@"12"] || [strRep isEqualToString:@"13"]) {
        ordinal = @"th";
    } else if ([lastDigit isEqualToString:@"1"]) {
        ordinal = @"st";
    } else if ([lastDigit isEqualToString:@"2"]) {
        ordinal = @"nd";
    } else if ([lastDigit isEqualToString:@"3"]) {
        ordinal = @"rd";
    } else {
        ordinal = @"th";
    }

    return [NSString stringWithFormat:@"%@%@", strRep, ordinal];
}

@end

Instantiate this as an Interface Builder object and attach the Text Field's formatter outlet to it. For finer control (such as setting maximum and minimum values, you should create an instance of the formatter, set the properties as you wish and attach it to text field using it's setFormatter: method.

You can download the class from GitHub (including an example project)

Upvotes: 14

Dave DeLong
Dave DeLong

Reputation: 243146

I'm not aware of this capability. However, it's possible to do this yourself. In English, the ordinal (th, st, nd, rd, etc) has a really simple pattern:

If the number ends with: => Use:

  • 0 => th
  • 1 => st
  • 2 => nd
  • 3 => rd
  • 4 => th
  • 5 => th
  • 6 => th
  • 7 => th
  • 8 => th
  • 9 => th
  • 11 => th
  • 12 => th
  • 13 => th

This will not spell out the word for you, but it will allow you to do something like: "42nd", "1,340,697th", etc.

This gets more complicated if you need it localized.

Upvotes: 3

Alex
Alex

Reputation: 933

The following example demonstrates how to handle any number. It's in c# however it can easily converted to any language.

http://www.bytechaser.com/en/functions/b6yhfyxh78/convert-number-to-ordinal-like-1st-2nd-in-c-sharp.aspx

Upvotes: 2

Related Questions