มีวิธีง่ายๆในการสร้าง ordinals ใน C # หรือไม่?


202

มีวิธีที่ง่ายใน C # ในการสร้างOrdinalsสำหรับตัวเลขหรือไม่? ตัวอย่างเช่น:

  • 1 ส่งคืนที่ 1
  • 2 ส่งคืน 2nd
  • 3 ส่งคืน 3
  • ... ฯลฯ

สิ่งนี้สามารถทำได้ผ่านString.Format()หรือมีฟังก์ชั่นใด ๆ ที่สามารถทำได้?

คำตอบ:


310

หน้านี้แสดงรายการที่สมบูรณ์ของกฎการจัดรูปแบบตัวเลขที่กำหนดเองทั้งหมด:

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

อย่างที่คุณเห็นไม่มีอะไรในนั้นเกี่ยวกับลำดับดังนั้นจึงไม่สามารถทำได้โดยใช้ String รูปแบบ อย่างไรก็ตามการเขียนฟังก์ชั่นก็ทำได้ไม่ยากนัก

public static string AddOrdinal(int num)
{
    if( num <= 0 ) return num.ToString();

    switch(num % 100)
    {
        case 11:
        case 12:
        case 13:
            return num + "th";
    }

    switch(num % 10)
    {
        case 1:
            return num + "st";
        case 2:
            return num + "nd";
        case 3:
            return num + "rd";
        default:
            return num + "th";
    }
}

อัปเดต: ไม่มีคำสั่งทางเทคนิคสำหรับ <= 0 ดังนั้นฉันจึงอัปเดตโค้ดด้านบน ยังลบToString()วิธีการซ้ำซ้อน

โปรดทราบว่านี่ไม่ได้เป็นสากล ฉันไม่รู้เลยว่ารูปลักษณ์ของเลขในภาษาอื่น ๆ เป็นอย่างไร


2
Assert.AreEqual ("0", AddOrdinal (0)); ดูwisegeek.com/what-is-an-ordinal-number.htm
si618

2
ใช้วิธีการขยาย (หรือสิ่งที่เรียกว่า - ดูคำตอบของ @ Stu) จะทำงานได้ดีที่นี่ @Si การเพิ่มเงื่อนไขนั้นจะง่ายมากหากจำเป็น
แปลกหน้า

12
ลืมเกี่ยวกับ '11, 12th 13' ... ควรเป็นคำถามสัมภาษณ์ :-)
Holf

2
ใช่โปรแกรมเมอร์กันแปลก ๆ ;)
samjudson

2
@IanWarburton ไม่มีการซ้ำซ้อนเนื่องจากจะมีการตีกลับคำสั่งเดียวเท่านั้น หากคุณไม่มีความสุขกับคำตอบโปรดให้ข้อมูลของคุณเองและแสดงวิธี "เหมาะสม" ในการทำเช่นนี้และทำไมจึงเป็นเรื่องสำคัญ
B2K

73

จำความเป็นสากล!

วิธีแก้ปัญหาที่นี่ใช้งานได้กับภาษาอังกฤษเท่านั้น สิ่งต่าง ๆ มีความซับซ้อนมากขึ้นถ้าคุณต้องการสนับสนุนภาษาอื่น

ตัวอย่างเช่นในภาษาสเปน "วันที่ 1" จะเขียนเป็น "1.o", "1.a", "1.os" หรือ "1.as" ขึ้นอยู่กับว่าสิ่งที่คุณกำลังนับนั้นเป็นผู้ชายผู้หญิงหรือพหูพจน์ !

ดังนั้นหากซอฟต์แวร์ของคุณต้องการสนับสนุนภาษาต่าง ๆ พยายามหลีกเลี่ยงกฎ


7
@ Andomar: "ผู้อ่าน 2 คนแรก" => ในอิตาลี (และสเปนด้วยฉันคิดว่า) "แรก" เป็นพหูพจน์ที่นี่ ดังนั้นคุณจึงมีความเป็นชายเอกพจน์ Singulare ผู้หญิงพหูพจน์ผู้ชายพหูพจน์ของผู้หญิง; อาจจะมีบางภาษาที่เป็นกลางเช่นกัน (การแยกแยะสิ่งต่าง ๆ จากผู้ชาย / สัตว์)
M.Turrini

2
ที่กล่าวว่าคุณไม่จำเป็นต้องหลีกเลี่ยงกฎ: รวมไว้ในการแปลเมื่อคุณรู้ทุกกรณีที่คุณอาจเผชิญหรือ (ทำให้ลูกค้าของคุณ) ยอมรับข้อ จำกัด บางอย่าง
M.Turrini

26
สิ่งนี้อธิบายได้ว่าเหตุใดทีมงาน. NET นำการเพิ่มไปยังตัวจัดรูปแบบ DateTime
Chris S

moment.js มีฟังก์ชั่นการจัดรูปแบบ "ปกติ" ตามสถานที่เกิดเหตุเพื่อให้สามารถทำได้และหวังว่าพวกเขาจะทำมันใน. NET สำหรับ DateTime
Guillaume86

5
มันจะง่ายมากถ้าคุณใช้ "." ตัวละครสำหรับ ordinals เช่นที่เราทำในภาษาเยอรมัน)))) 1. 2. 3. 4. 5. , ฯลฯ แม้ว่าอัลกอริทึมจะน่าสนใจมากขึ้นถ้าใครจะเขียนหมายเลขและต้องผนวกการผันใน 4 กรณีทางไวยากรณ์ที่มี 3 บทความที่แตกต่างกันนอกเหนือจากกรณีเอกพจน์และพหูพจน์ของ 12 ชุดที่แตกต่างกัน ลองคิดดูสิรัสเซียไม่มีอีก 2 คนบวกกับคำพูดและภาษานอร์ดิกบางภาษามี 15 ฉันคิดว่า ฉันชอบที่จะเห็นการใช้งานใน. NET
Stefan Steiger

22

เวอร์ชั่นของ Stu's และ samjudson เวอร์ชั่น Jesse ของฉัน :)

การทดสอบหน่วยที่รวมเพื่อแสดงว่าคำตอบที่ยอมรับนั้นไม่ถูกต้องเมื่อหมายเลข <1

    /// <summary>
    /// Get the ordinal value of positive integers.
    /// </summary>
    /// <remarks>
    /// Only works for english-based cultures.
    /// Code from: http://stackoverflow.com/questions/20156/is-there-a-quick-way-to-create-ordinals-in-c/31066#31066
    /// With help: http://www.wisegeek.com/what-is-an-ordinal-number.htm
    /// </remarks>
    /// <param name="number">The number.</param>
    /// <returns>Ordinal value of positive integers, or <see cref="int.ToString"/> if less than 1.</returns>
    public static string Ordinal(this int number)
    {
        const string TH = "th";
        string s = number.ToString();

        // Negative and zero have no ordinal representation
        if (number < 1)
        {
            return s;
        }

        number %= 100;
        if ((number >= 11) && (number <= 13))
        {
            return s + TH;
        }

        switch (number % 10)
        {
            case 1: return s + "st";
            case 2: return s + "nd";
            case 3: return s + "rd";
            default: return s + TH;
        }
    }

    [Test]
    public void Ordinal_ReturnsExpectedResults()
    {
        Assert.AreEqual("-1", (1-2).Ordinal());
        Assert.AreEqual("0", 0.Ordinal());
        Assert.AreEqual("1st", 1.Ordinal());
        Assert.AreEqual("2nd", 2.Ordinal());
        Assert.AreEqual("3rd", 3.Ordinal());
        Assert.AreEqual("4th", 4.Ordinal());
        Assert.AreEqual("5th", 5.Ordinal());
        Assert.AreEqual("6th", 6.Ordinal());
        Assert.AreEqual("7th", 7.Ordinal());
        Assert.AreEqual("8th", 8.Ordinal());
        Assert.AreEqual("9th", 9.Ordinal());
        Assert.AreEqual("10th", 10.Ordinal());
        Assert.AreEqual("11th", 11.Ordinal());
        Assert.AreEqual("12th", 12.Ordinal());
        Assert.AreEqual("13th", 13.Ordinal());
        Assert.AreEqual("14th", 14.Ordinal());
        Assert.AreEqual("20th", 20.Ordinal());
        Assert.AreEqual("21st", 21.Ordinal());
        Assert.AreEqual("22nd", 22.Ordinal());
        Assert.AreEqual("23rd", 23.Ordinal());
        Assert.AreEqual("24th", 24.Ordinal());
        Assert.AreEqual("100th", 100.Ordinal());
        Assert.AreEqual("101st", 101.Ordinal());
        Assert.AreEqual("102nd", 102.Ordinal());
        Assert.AreEqual("103rd", 103.Ordinal());
        Assert.AreEqual("104th", 104.Ordinal());
        Assert.AreEqual("110th", 110.Ordinal());
        Assert.AreEqual("111th", 111.Ordinal());
        Assert.AreEqual("112th", 112.Ordinal());
        Assert.AreEqual("113th", 113.Ordinal());
        Assert.AreEqual("114th", 114.Ordinal());
        Assert.AreEqual("120th", 120.Ordinal());
        Assert.AreEqual("121st", 121.Ordinal());
        Assert.AreEqual("122nd", 122.Ordinal());
        Assert.AreEqual("123rd", 123.Ordinal());
        Assert.AreEqual("124th", 124.Ordinal());
    }

15

เรียบง่ายสะอาดและรวดเร็ว

    private static string GetOrdinalSuffix(int num)
    {
        if (num.ToString().EndsWith("11")) return "th";
        if (num.ToString().EndsWith("12")) return "th";
        if (num.ToString().EndsWith("13")) return "th";
        if (num.ToString().EndsWith("1")) return "st";
        if (num.ToString().EndsWith("2")) return "nd";
        if (num.ToString().EndsWith("3")) return "rd";
        return "th";
    }

หรือดีกว่ายังเป็นวิธีการขยาย

public static class IntegerExtensions
{
    public static string DisplayWithSuffix(this int num)
    {
        if (num.ToString().EndsWith("11")) return num.ToString() + "th";
        if (num.ToString().EndsWith("12")) return num.ToString() + "th";
        if (num.ToString().EndsWith("13")) return num.ToString() + "th";
        if (num.ToString().EndsWith("1")) return num.ToString() + "st";
        if (num.ToString().EndsWith("2")) return num.ToString() + "nd";
        if (num.ToString().EndsWith("3")) return num.ToString() + "rd";
        return num.ToString() + "th";
    }
}

ตอนนี้คุณสามารถโทร

int a = 1;
a.DisplayWithSuffix(); 

หรือแม้กระทั่งโดยตรง

1.DisplayWithSuffix();

14

คุณจะต้องม้วนตัวเอง จากด้านบนของหัวของฉัน:

public static string Ordinal(this int number)
{
  var work = number.ToString();
  if ((number % 100) == 11 || (number % 100) == 12 || (number % 100) == 13)
    return work + "th";
  switch (number % 10)
  {
    case 1: work += "st"; break;
    case 2: work += "nd"; break;
    case 3: work += "rd"; break;
    default: work += "th"; break;
  }
  return work;
}

จากนั้นคุณสามารถทำได้

Console.WriteLine(432.Ordinal());

แก้ไขสำหรับข้อยกเว้น 11/12/13 ฉันพูดจากส่วนบนของหัวของฉัน :-)

แก้ไขแล้วสำหรับ 1,011 - คนอื่น ๆ ได้แก้ไขสิ่งนี้แล้วเพียงต้องการให้แน่ใจว่าคนอื่นไม่คว้ารุ่นที่ไม่ถูกต้องนี้


12

ฉันชอบองค์ประกอบจากทั้งวิธีของStuและsamjudsonและทำงานร่วมกันในสิ่งที่ฉันคิดว่าเป็นคอมโบที่ใช้งานได้:

    public static string Ordinal(this int number)
    {
        const string TH = "th";
        var s = number.ToString();

        number %= 100;

        if ((number >= 11) && (number <= 13))
        {
            return s + TH;
        }

        switch (number % 10)
        {
            case 1:
                return s + "st";
            case 2:
                return s + "nd";
            case 3:
                return s + "rd";
            default:
                return s + TH;
        }
    }

1
เหตุผลเบื้องหลังการใช้ค่าคงที่สำหรับ "th" คืออะไร
nickf

เพราะมันใช้สองครั้งในรหัส เพียงแค่ใช้ภูมิปัญญาเก่าแก่ที่คุณไม่ควรทำซ้ำด้วยตัวคุณเอง :) ในกรณีนี้. NET runtime ควรสร้างเพียงหนึ่งสำเนาของสตริงในขณะที่มีสอง "th" ในรหัสจะมีสองสายที่สร้างขึ้น และอ้างอิงในหน่วยความจำ
Jesse C. Slicer

25
และหากมูลค่าของ TH เปลี่ยนแปลงตลอดเวลาคุณจะถูกตั้งค่า
Eclipse

7
@Jesse - คุณได้รับ +1 ของฉัน แต่ฉันไม่เชื่อว่า. NET จัดการกับสตริงด้วยวิธีนี้โปรดดูyoda.arachsys.com/csharp/strings.html#interningการอ่านของฉันที่อ้างอิงถึงตัวอักษร "th" แต่ละตัว จะอ้างอิงหน่วยความจำบิตเดียวกัน แต่ฉันเห็นด้วยกับ DRY :)
si618

4
การลบการทำซ้ำเช่นนี้เป็นเพียงการขัดขวางการอ่านที่ฉันคิดว่าดังนั้นความสับสน "ทำไม TH?" ฉันไม่คิดว่า DRY ควรถูกตีความว่าเป็น 'ลบการทำซ้ำทั้งหมดไม่ว่าจะมีราคาเท่าใด'
SeeNoWeevil

8

ในขณะที่ฉันยังไม่ได้ทำการเปรียบเทียบคุณควรจะได้ประสิทธิภาพที่ดีขึ้นโดยการหลีกเลี่ยงคำสั่งกรณีตามเงื่อนไขทั้งหมด

นี่คือ java แต่พอร์ตไปยัง C # นั้นเล็กน้อย:

public class NumberUtil {
  final static String[] ORDINAL_SUFFIXES = {
    "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
  };

  public static String ordinalSuffix(int value) {
    int n = Math.abs(value);
    int lastTwoDigits = n % 100;
    int lastDigit = n % 10;
    int index = (lastTwoDigits >= 11 && lastTwoDigits <= 13) ? 0 : lastDigit;
    return ORDINAL_SUFFIXES[index];
  }

  public static String toOrdinal(int n) {
    return new StringBuffer().append(n).append(ordinalSuffix(n)).toString();
  }
}

หมายเหตุการลดเงื่อนไขและการใช้การค้นหาอาร์เรย์ควรเร่งประสิทธิภาพหากสร้างกฎจำนวนมากในลูปที่แน่น อย่างไรก็ตามฉันยอมรับว่านี่ไม่สามารถอ่านได้เหมือนกับโซลูชันคำสั่ง case


ขออภัยที่ฉันเปรียบเทียบผลิตภัณฑ์นี้ใน C # เวอร์ชันของคุณไม่เร็วกว่าโซลูชันของ si618
GY_

ตรวจสอบคำตอบนี้stackoverflow.com/a/58378465/2583579สำหรับการวัดประสิทธิภาพบางอย่าง
Dan Dohotaru

3

เช่นเดียวกับวิธีแก้ปัญหาของ Ryan แต่ขั้นพื้นฐานยิ่งกว่านั้นฉันแค่ใช้อาร์เรย์ธรรมดาและใช้วันนั้นเพื่อค้นหาลำดับที่ถูกต้อง:

private string[] ordinals = new string[] {"","st","nd","rd","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","st","nd","rd","th","th","th","th","th","th","th","st" };
DateTime D = DateTime.Now;
String date = "Today's day is: "+ D.Day.ToString() + ordinals[D.Day];

ฉันไม่ได้มีความต้องการ แต่ฉันคิดว่าคุณสามารถใช้หลายมิติอาเรย์ถ้าคุณต้องการได้รับการสนับสนุนหลายภาษา

จากสิ่งที่ฉันสามารถจำได้จากวัน Uni ของฉันวิธีนี้ต้องใช้ความพยายามน้อยที่สุดจากเซิร์ฟเวอร์


2

ฉันใช้คลาสส่วนขยายนี้:

public static class Int32Extensions
{
    public static string ToOrdinal(this int i)
    {
        return (i + "th")
            .Replace("1th", "1st")
            .Replace("2th", "2nd")
            .Replace("3th", "3rd");
    }
}

11st, 12nd, 13rd
Kcoder

2

ร้องขอคำตอบของ samjudson รุ่น "less redundancy" ...

public static string AddOrdinal(int number)
{
    if (number <= 0) return number.ToString();

    string GetIndicator(int num)
    {
        switch (num % 100)
        {
            case 11:
            case 12:
            case 13:
                return "th";
        }

        switch (num % 10)
        {
            case 1:
                return "st";
            case 2:
                return "nd";
            case 3:
                return "rd";
            default:
                return "th";
        }
    }

    return number + GetIndicator(number);
}

2
ฉันจะเปิดเผย "GetIndicator" เป็นpublic staticและเปลี่ยนชื่อเป็นชื่อที่ช่วยในการจำเพิ่มเติม (เช่น "OrdinalSuffix") ผู้โทรอาจต้องการหมายเลขส่วนในรูปแบบที่ต่างกัน (เช่นมีคอมมา)
ทอม

2
        private static string GetOrd(int num) => $"{num}{(!(Range(11, 3).Any(n => n == num % 100) ^ Range(1, 3).All(n => n != num % 10)) ? new[] { "ˢᵗ", "ⁿᵈ", "ʳᵈ" }[num % 10 - 1] : "ᵗʰ")}";

หากใครกำลังมองหาสายการบินหนึ่ง: p


1
public static string OrdinalSuffix(int ordinal)
{
    //Because negatives won't work with modular division as expected:
    var abs = Math.Abs(ordinal); 

    var lastdigit = abs % 10; 

    return 
        //Catch 60% of cases (to infinity) in the first conditional:
        lastdigit > 3 || lastdigit == 0 || (abs % 100) - lastdigit == 10 ? "th" 
            : lastdigit == 1 ? "st" 
            : lastdigit == 2 ? "nd" 
            : "rd";
}

1

แก้ไข : YM_Industries ชี้ให้เห็นในความคิดเห็นคำตอบของ samjudsonทำงานได้มากกว่า 1,000 ความคิดเห็นของ nickf ดูเหมือนจะหายไปและฉันจำไม่ได้ว่าปัญหาที่ฉันเห็นคืออะไร ปล่อยคำตอบนี้ไว้ที่นี่เพื่อเปรียบเทียบเวลา

สิ่งที่น่ากลัวมากมายเหล่านี้ใช้ไม่ได้กับตัวเลข> 999 ตามที่nickfชี้ให้เห็นในความคิดเห็น (แก้ไข: ตอนนี้ขาดหายไป)

นี่เป็นรุ่นที่ตามออกรุ่นที่ปรับเปลี่ยนsamjudson 's คำตอบที่ได้รับการยอมรับที่ไม่

public static String GetOrdinal(int i)
{
    String res = "";

    if (i > 0)
    {
        int j = (i - ((i / 100) * 100));

        if ((j == 11) || (j == 12) || (j == 13))
            res = "th";
        else
        {
            int k = i % 10;

            if (k == 1)
                res = "st";
            else if (k == 2)
                res = "nd";
            else if (k == 3)
                res = "rd";
            else
                res = "th";
        }
    }

    return i.ToString() + res;
}

นอกจากนี้คำตอบของShahzad Qureshiโดยใช้การจัดการสตริงทำงานได้ดี แต่ก็มีโทษประสิทธิภาพ สำหรับการสร้างสิ่งเหล่านี้จำนวนมากโปรแกรมตัวอย่าง LINQPad ทำให้สตริงรุ่น 6-7 เท่าช้ากว่าเลขจำนวนเต็มนี้ (แม้ว่าคุณจะต้องสร้างจำนวนมากเพื่อสังเกตให้เห็น)

ตัวอย่าง LINQPad:

void Main()
{
    "Examples:".Dump();

    foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 10000013 })
        Stuff.GetOrdinal(i).Dump();

    String s;

    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

    for(int iter = 0; iter < 100000; iter++)
        foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })
            s = Stuff.GetOrdinal(i);

    "Integer manipulation".Dump();
    sw.Elapsed.Dump();

    sw.Restart();

    for(int iter = 0; iter < 100000; iter++)
        foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })
            s = (i.ToString() + Stuff.GetOrdinalSuffix(i));

    "String manipulation".Dump();
    sw.Elapsed.Dump();
}

public class Stuff
{
        // Use integer manipulation
        public static String GetOrdinal(int i)
        {
                String res = "";

                if (i > 0)
                {
                        int j = (i - ((i / 100) * 100));

                        if ((j == 11) || (j == 12) || (j == 13))
                                res = "th";
                        else
                        {
                                int k = i % 10;

                                if (k == 1)
                                        res = "st";
                                else if (k == 2)
                                        res = "nd";
                                else if (k == 3)
                                        res = "rd";
                                else
                                        res = "th";
                        }
                }

                return i.ToString() + res;
        }

        // Use string manipulation
        public static string GetOrdinalSuffix(int num)
        {
                if (num.ToString().EndsWith("11")) return "th";
                if (num.ToString().EndsWith("12")) return "th";
                if (num.ToString().EndsWith("13")) return "th";
                if (num.ToString().EndsWith("1")) return "st";
                if (num.ToString().EndsWith("2")) return "nd";
                if (num.ToString().EndsWith("3")) return "rd";
                return "th";
        }
}

ฉันไม่พบความคิดเห็นของ @ nickf เกิดอะไรขึ้นกับคำตอบของ samjudson ดูเหมือนว่าฉันจะจัดการกับตัวเลขที่สูงกว่า 1,000 ได้ดีในขณะที่อ่านได้ดีกว่าของคุณ
Joshua Walsh

1
มันเป็นความคิดเห็นที่ยุติธรรมฉันเพิ่งรันชุดทดสอบและฉันไม่พบปัญหาใด ๆ ดูเหมือนจะไม่ได้มีการแก้ไขคำตอบของแซมอย่างใดอย่างหนึ่งดังนั้นฉันสามารถจินตนาการได้ว่าฉันกำลังจะบ้า ฉันได้แก้ไขคำตอบของฉันเพื่อสะท้อนสิ่งนั้นแล้ว
Whelkaholism

1
ฮ่าฮ่าพวกเราทุกคนมีช่วงเวลาแบบนั้นใช่มั้ย เรามองไปที่รหัสเก่าและไป "ทำไมฉันถึงเขียนนรกนี้"
Joshua Walsh

1

ตามคำตอบอื่น ๆ :

public static string Ordinal(int n)
{   
    int     r = n % 100,     m = n % 10;

    return (r<4 || r>20) && (m>0 && m<4) ? n+"  stndrd".Substring(m*2,2) : n+"th";                                              
}

3
ที่ 1 สถานที่: คำตอบที่คลุมเครือที่สุด "ไม่จำเป็น": ขนาดและประสิทธิภาพของโค้ดนั้นไม่คุ้มกับค่าที่อ่านได้ "Cryptic": จำเป็นต้องมีการแปลที่สำคัญในการจับคู่กับข้อกำหนด "บุคคลทั่วไป"
ทอม

0

FWIW สำหรับ MS-SQL นิพจน์นี้จะทำงาน เก็บ WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th') แรกเป็นรายการแรกในรายการเนื่องจากจะขึ้นอยู่กับการทดลองต่อหน้าผู้อื่น

CASE
  WHEN num % 100 IN (11, 12, 13) THEN 'th' -- must be tried first
  WHEN num % 10 = 1 THEN 'st'
  WHEN num % 10 = 2 THEN 'nd'
  WHEN num % 10 = 3 THEN 'rd'
  ELSE 'th'
END AS Ordinal

สำหรับ Excel:

=MID("thstndrdth",MIN(9,2*RIGHT(A1)*(MOD(A1-11,100)>2)+1),2)

นิพจน์ (MOD(A1-11,100)>2)คือ TRUE (1) สำหรับตัวเลขทั้งหมดยกเว้นการลงท้ายด้วย11,12,13(FALSE = 0) ดังนั้น2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)สิ้นสุดเป็น 1 สำหรับ 11/12/13 มิฉะนั้น:
1 ประเมินเป็น 3
2 ถึง 5,
3 ถึง 7
อื่น ๆ : 9
- และอักขระ 2 ตัวที่ต้องการถูกเลือกตั้งแต่"thstndrdth"เริ่มต้นจากตำแหน่งนั้น

หากคุณต้องการแปลงที่เป็นธรรมโดยตรงกับ SQL สิ่งนี้ใช้ได้กับฉันสำหรับค่าทดสอบจำนวนหนึ่ง:

DECLARE @n as int
SET @n=13
SELECT SubString(  'thstndrdth'
                 , (SELECT MIN(value) FROM
                     (SELECT 9 as value UNION
                      SELECT 1+ (2* (ABS(@n) % 10)  *  CASE WHEN ((ABS(@n)+89) % 100)>2 THEN 1 ELSE 0 END)
                     ) AS Mins
                   )
                 , 2
                )

0

นี่คือการนำไปปฏิบัติdartและสามารถแก้ไขได้ตามภาษา

String getOrdinalSuffix(int num){
    if (num.toString().endsWith("11")) return "th";
    if (num.toString().endsWith("12")) return "th";
    if (num.toString().endsWith("13")) return "th";
    if (num.toString().endsWith("1")) return "st";
    if (num.toString().endsWith("2")) return "nd";
    if (num.toString().endsWith("3")) return "rd";
    return "th";
}

0

ในขณะที่มีคำตอบที่ดีมากมายที่นี่ฉันคิดว่ามีที่ว่างสำหรับอีกห้องหนึ่งคราวนี้ขึ้นอยู่กับการจับคู่รูปแบบถ้าไม่ใช่อย่างอื่นแล้วอย่างน้อยก็เป็นที่อ่านได้ง่าย

    public static string Ordinals1(this int number)
    {
        switch (number)
        {
            case int p when p % 100 == 11:
            case int q when q % 100 == 12:
            case int r when r % 100 == 13:
                return $"{number}th";
            case int p when p % 10 == 1:
                return $"{number}st";
            case int p when p % 10 == 2:
                return $"{number}nd";
            case int p when p % 10 == 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

และอะไรทำให้โซลูชั่นนี้พิเศษ? ไม่มีอะไรนอกจากความจริงที่ว่าฉันกำลังเพิ่มข้อควรพิจารณาด้านประสิทธิภาพสำหรับโซลูชันอื่น ๆ

ฉันสงสัยว่าการแสดงมีความสำคัญจริง ๆ สำหรับสถานการณ์นี้ (ผู้ที่ต้องการเลขลำดับเป็นล้านจริงๆ) แต่อย่างน้อยก็มีการเปรียบเทียบบางอย่างที่ต้องนำมาพิจารณา ...

1 ล้านรายการสำหรับการอ้างอิง (ระยะของคุณอาจแตกต่างกันไปตามรายละเอียดของเครื่อง)

ด้วยการจับคู่รูปแบบและส่วน (คำตอบนี้)

~ 622 ms

ด้วยการจับคู่รูปแบบและสตริง (คำตอบนี้)

~ 1967 มิลลิวินาที

พร้อมสวิตช์สองตัวและดิวิชั่น (คำตอบที่ยอมรับได้)

~ 637 ms

ด้วยสวิตช์และดิวิชั่นหนึ่ง (คำตอบอื่น)

~ 725 ms

void Main()
{
    var timer = new Stopwatch();
    var numbers = Enumerable.Range(1, 1000000).ToList();

    // 1
    timer.Reset();
    timer.Start();
    var results1 = numbers.Select(p => p.Ordinals1()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and divisions");

    // 2
    timer.Reset();
    timer.Start();
    var results2 = numbers.Select(p => p.Ordinals2()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and strings");

    // 3
    timer.Reset();
    timer.Start();
    var results3 = numbers.Select(p => p.Ordinals3()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with two switches and divisons");

    // 4
    timer.Reset();
    timer.Start();
    var results4 = numbers.Select(p => p.Ordinals4()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with one switche and divisons");
}

public static class Extensions
{
    public static string Ordinals1(this int number)
    {
        switch (number)
        {
            case int p when p % 100 == 11:
            case int q when q % 100 == 12:
            case int r when r % 100 == 13:
                return $"{number}th";
            case int p when p % 10 == 1:
                return $"{number}st";
            case int p when p % 10 == 2:
                return $"{number}nd";
            case int p when p % 10 == 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals2(this int number)
    {
        var text = number.ToString();
        switch (text)
        {
            case string p when p.EndsWith("11"):
                return $"{number}th";
            case string p when p.EndsWith("12"):
                return $"{number}th";
            case string p when p.EndsWith("13"):
                return $"{number}th";
            case string p when p.EndsWith("1"):
                return $"{number}st";
            case string p when p.EndsWith("2"):
                return $"{number}nd";
            case string p when p.EndsWith("3"):
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals3(this int number)
    {
        switch (number % 100)
        {
            case 11:
            case 12:
            case 13:
                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";
        }
    }

    public static string Ordinals4(this int number)
    {
        var ones = number % 10;
        var tens = Math.Floor(number / 10f) % 10;
        if (tens == 1)
        {
            return $"{number}th";
        }

        switch (ones)
        {
            case 1:
                return $"{number}th";
            case 2:
                return $"{number}nd";
            case 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }
}

0

อีกหนึ่งซับ แต่ไม่มีการเปรียบเทียบโดยการจัดทำดัชนีผลลัพธ์ regex ลงในอาร์เรย์เท่านั้น

public static string GetOrdinalSuffix(int input)
{
    return new []{"th", "st", "nd", "rd"}[Convert.ToInt32("0" + Regex.Match(input.ToString(), "(?<!1)[1-3]$").Value)];
}

รุ่น PowerShell สามารถตัดให้สั้นลงไปอีก:

function ord($num) { return ('th','st','nd','rd')[[int]($num -match '(?<!1)[1-3]$') * $matches[0]] }

0

อีก 1 ซับ

public static string Ordinal(this int n)
{    
 return n + (new [] {"st","nd","rd" }.ElementAtOrDefault((((n + 90) % 100 - 10) % 10 - 1)) ?? "th");
}

-2

นี่คือคลาส DateTime Extension คัดลอกวางและเพลิดเพลิน

คลาสแบบคงที่สาธารณะ DateTimeExtensions {

    public static string ToStringWithOrdinal(this DateTime d)
    {
        var result = "";
        bool bReturn = false;            

        switch (d.Day % 100)
        {
            case 11:
            case 12:
            case 13:
                result = d.ToString("dd'th' MMMM yyyy");
                bReturn = true;
                break;
        }

        if (!bReturn)
        {
            switch (d.Day % 10)
            {
                case 1:
                    result = d.ToString("dd'st' MMMM yyyy");
                    break;
                case 2:
                    result = d.ToString("dd'nd' MMMM yyyy");
                    break;
                case 3:
                    result = d.ToString("dd'rd' MMMM yyyy");
                    break;
                default:
                    result = d.ToString("dd'th' MMMM yyyy");
                    break;
            }

        }

        if (result.StartsWith("0")) result = result.Substring(1);
        return result;
    }
}

ผลลัพธ์ :

9 ตุลาคม 2014


คุณกำลังทำซ้ำ: ก) สตริงรูปแบบวันที่ (X5) และ b) ส่วนที่เหลือทั้งหมดของวิธีการ (เมื่อมีกรณีการใช้งานที่น่าจะเกิดขึ้น (หากยังไม่ได้ดำเนินการ) ว่าต้องการส่วนต่อท้ายแบบเลขชี้กำลังสำหรับวันที่ไม่ใช่ของเดือน วัตถุประสงค์หรือแม้กระทั่งวันที่เดือนที่มีสตริงรูปแบบวันที่ที่แตกต่างกัน) ใช้วิธี "OrdinalSuffix" ที่ฉันแนะนำให้เปิดเผยจาก Ian Warburton ในวันที่ 6 เม.ย. '17 ที่ 16:32 คำตอบ ( stackoverflow.com/questions/20156/… )
ทอม

-3

อีกทางเลือกหนึ่งที่ฉันใช้ตามข้อเสนอแนะอื่น ๆ ทั้งหมด แต่ไม่จำเป็นต้องมีปลอกพิเศษ:

    public static string DateSuffix(int day)
    {
        if (day == 11 | day == 12 | day == 13) return "th";
        Math.DivRem(day, 10, out day);
        switch (day)
        {
            case 1:
                return "st";
            case 2:
                return "nd";
            case 3:
                return "rd";
            default:
                return "th";
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.