Có một cách dễ dàng để tạo ra các thứ tự trong C #?


202

Có một cách dễ dàng trong C # để tạo Ordinals đối với một số? Ví dụ:

  • 1 trả về 1
  • 2 trả về 2
  • 3 trả về thứ 3
  • ...Vân vân

Điều này có thể được thực hiện thông qua String.Format()hoặc có bất kỳ chức năng có sẵn để làm điều này?

Câu trả lời:


311

Trang này cung cấp cho bạn một danh sách đầy đủ tất cả các quy tắc định dạng số tùy chỉnh:

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

Như bạn có thể thấy, không có gì trong đó về các thứ tự, vì vậy không thể thực hiện được bằng String.Format. Tuy nhiên nó không thực sự khó để viết một chức năng để làm điều đó.

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";
    }
}

Cập nhật: Về mặt kỹ thuật Thông thường không tồn tại cho <= 0, vì vậy tôi đã cập nhật mã ở trên. Cũng loại bỏ dư thừaToString() phương pháp .

Cũng lưu ý, điều này không được quốc tế hóa. Tôi không biết thứ tự trông như thế nào trong các ngôn ngữ khác.


2
Khẳng định.AreEqual ("0", AddOrdinal (0)); Xem Wisegeek.com/what-is-an-ordinal-number.htmlm
si618

2
Sử dụng một phương pháp mở rộng (hoặc bất cứ điều gì nó được gọi - xem câu trả lời của @ Stu) sẽ hoạt động tốt ở đây. @Si, Thêm điều kiện đó sẽ rất dễ dàng nếu cần.
strager

12
Quên về 'ngày 11, ngày 12 tháng 12' ... nên là một câu hỏi phỏng vấn. :-)
Holf

2
Vâng, lập trình viên rất kỳ lạ;)
samjudson

2
@IanWarburton Không có dự phòng vì chỉ có một tuyên bố hoàn trả duy nhất sẽ được nhấn. Nếu bạn không hài lòng với câu trả lời, vui lòng cung cấp cho riêng bạn, chỉ cho chúng tôi cách "phù hợp" để làm điều này và tại sao nó quan trọng.
B2K

73

Nhớ quốc tế hóa!

Các giải pháp ở đây chỉ làm việc cho tiếng Anh. Mọi thứ trở nên phức tạp hơn rất nhiều nếu bạn cần hỗ trợ các ngôn ngữ khác.

Ví dụ: trong tiếng Tây Ban Nha "1st" sẽ được viết là "1.o", "1.a", "1.os" hoặc "1.as" tùy thuộc vào việc bạn đang đếm là nam tính, nữ tính hay số nhiều !

Vì vậy, nếu phần mềm của bạn cần hỗ trợ các ngôn ngữ khác nhau, hãy cố gắng tránh các chức năng.


7
@ Andomar: "2 độc giả đầu tiên" => bằng tiếng Ý (và cả tiếng Tây Ban Nha, tôi cho rằng) "đầu tiên" là số nhiều ở đây. Vì vậy, bạn có số ít nam tính, singulare nữ tính, số nhiều nam tính, số nhiều nữ tính; có thể một số ngôn ngữ cũng có trường hợp trung lập (những thứ xa cách với đàn ông / động vật)
M.Turrini

2
Điều đó nói rằng, bạn không cần phải tránh các quy tắc: bao gồm chúng trong nội địa hóa, một khi bạn biết tất cả các trường hợp bạn có thể gặp phải, hoặc (làm cho khách hàng của bạn) chấp nhận một số hạn chế.
M.Turrini

26
Điều này giải thích lý do tại sao nhóm .NET tránh xa việc thêm nó vào các trình định dạng DateTime
Chris S

Moment.js có chức năng định dạng "thứ tự" theo ngôn ngữ nên có vẻ như có thể thực hiện được, cũng ước họ có thể thực hiện nó trong .NET cho DateTime
Guillaume86

5
Tất cả sẽ rất đơn giản nếu tất cả các bạn sử dụng dấu "." ký tự cho các số thứ tự, giống như chúng ta làm trong tiếng Đức)))) 1. 2. 3. 4. 5., v.v ... Mặc dù thuật toán sẽ thú vị hơn nhiều nếu người ta viết ra số đó và phải nối thêm 4 trường hợp ngữ pháp với 3 bài viết khác nhau, ngoài các trường hợp số ít và số nhiều của 12 kết hợp khác nhau. Hãy nghĩ về nó, đừng người Nga có thêm 2, cộng với vocativ, và một số ngôn ngữ Bắc Âu có 15, tôi nghĩ vậy. Tôi rất thích thấy triển khai trong .NET.
Stefan Steiger

22

Phiên bản của tôi về phiên bản Jesse và samjudson của Jesse :)

Kiểm tra đơn vị bao gồm để chỉ ra rằng câu trả lời được chấp nhận là không chính xác khi số <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

Đơn giản, sạch sẽ, nhanh chóng

    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";
    }

Hoặc tốt hơn, như một phương pháp mở rộng

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";
    }
}

Bây giờ bạn chỉ có thể gọi

int a = 1;
a.DisplayWithSuffix(); 

hoặc thậm chí trực tiếp như

1.DisplayWithSuffix();

14

Bạn sẽ phải tự lăn. Từ đỉnh đầu của tôi:

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;
}

Bạn có thể làm

Console.WriteLine(432.Ordinal());

Đã chỉnh sửa cho 11/12/13 trường hợp ngoại lệ. Tôi nói từ trên đỉnh đầu của tôi :-)

Đã chỉnh sửa cho 1011 - những người khác đã sửa lỗi này rồi, chỉ muốn đảm bảo những người khác không lấy phiên bản không chính xác này.


12

Tôi thích các yếu tố từ các giải pháp của cả Stusamjudson và kết hợp chúng với nhau thành những gì tôi nghĩ là một kết hợp có thể sử dụng:

    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
lý do đằng sau việc sử dụng hằng số cho "th" là gì?
nickf

bởi vì nó được sử dụng hai lần trong mã. Chỉ cần sử dụng trí tuệ lâu đời mà bạn không nên lặp lại :) Trong trường hợp này, thời gian chạy .NET chỉ nên tạo một bản sao của chuỗi trong khi có hai "th" trong mã, sẽ có hai chuỗi được tạo và được tham chiếu trong bộ nhớ.
Jesse C. Choper

25
và ngoài ra, nếu giá trị của TH thay đổi, bạn sẽ được đặt.
Nhật thực

7
@Jlie - Bạn nhận được +1 của mình, nhưng tôi không tin .NET xử lý các chuỗi theo cách này, xem yoda.arachsys.com/csharp/strings.html#iterning , cách đọc của tôi là mỗi tham chiếu đến chữ "th" sẽ tham chiếu cùng một chút của bộ nhớ. Nhưng tôi đồng ý về DRY :)
si618

4
Tôi loại bỏ trùng lặp như thế này chỉ cản trở khả năng đọc tôi nghĩ, do đó nhầm lẫn "Tại sao TH?". Tôi không nghĩ DRY nên được hiểu là "loại bỏ tất cả các bản sao bất cứ giá nào".
Xem Không Weevil

8

Mặc dù tôi chưa đạt điểm chuẩn này nhưng bạn sẽ có thể có hiệu suất tốt hơn bằng cách tránh tất cả các báo cáo trường hợp có điều kiện.

Đây là java, nhưng một cổng tới C # là tầm thường:

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();
  }
}

Lưu ý, việc giảm các điều kiện và việc sử dụng tra cứu mảng sẽ tăng tốc hiệu suất nếu tạo ra nhiều lệnh trong một vòng lặp chặt chẽ. Tuy nhiên, tôi cũng thừa nhận rằng điều này không dễ đọc như giải pháp tuyên bố trường hợp.


Xin lỗi vì tôi đã điểm chuẩn này trong C #, phiên bản của bạn không nhanh hơn giải pháp của si618.
GY_

kiểm tra câu trả lời này stackoverflow.com/a/58378465/2583579 để biết một số điểm chuẩn
Dan Dohotaru

3

Tương tự như giải pháp của Ryan, nhưng thậm chí còn cơ bản hơn, tôi chỉ sử dụng một mảng đơn giản và sử dụng ngày để tra cứu thứ tự chính xác:

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];

Tôi chưa có nhu cầu, nhưng tôi cho rằng bạn có thể sử dụng một mảng đa chiều nếu bạn muốn có nhiều hỗ trợ ngôn ngữ.

Từ những gì tôi có thể nhớ từ những ngày Uni của mình, phương pháp này đòi hỏi nỗ lực tối thiểu từ máy chủ.


2

Tôi sử dụng lớp mở rộng này:

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

11, 12, 13
Kcoder

2

Phiên bản "ít dư thừa" được yêu cầu của câu trả lời của samjudson ...

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
Tôi sẽ đặt "GetTheicator" là một public staticvà đổi tên nó thành một cái tên dễ nhớ hơn (tức là "OrdinalSuffix"). Người gọi có thể muốn phần số ở các định dạng khác nhau (ví dụ như bằng dấu phẩy).
Tom

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] : "ᵗʰ")}";

Nếu bất cứ ai tìm kiếm một lót: 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

EDIT : Như YM_Industries chỉ ra trong bình luận, câu trả lời của samjudson DOES hoạt động với số lượng trên 1000, bình luận của nickf dường như đã biến mất và tôi không thể nhớ vấn đề mình gặp phải là gì. Để lại câu trả lời này ở đây cho thời gian so sánh.

Rất nhiều trong số này không hoạt động với số> 999, như nickf đã chỉ ra trong một nhận xét (EDIT: hiện đang thiếu).

Dưới đây là một phiên bản dựa trên một phiên bản sửa đổi của samjudson 's câu trả lời được chấp nhận mà không.

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;
}

Ngoài ra câu trả lời của Shahzad Qureshi sử dụng thao tác chuỗi hoạt động tốt, tuy nhiên nó có hình phạt hiệu suất. Để tạo ra nhiều thứ này, một chương trình ví dụ LINQPad làm cho phiên bản chuỗi chậm hơn 6-7 lần so với số nguyên này (mặc dù bạn phải tạo ra nhiều thứ để chú ý).

Ví dụ 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";
        }
}

Tôi không thể tìm thấy bình luận của @ nickf, có gì sai với câu trả lời của samjudson? Đối với tôi có vẻ như nó xử lý các con số trên 1000 chỉ tốt trong khi dễ đọc hơn nhiều so với của bạn.
Joshua Walsh

1
Đó là một nhận xét công bằng, tôi vừa chạy thử nghiệm và tôi không thể tìm thấy bất kỳ vấn đề nào. Dường như không có bất kỳ chỉnh sửa nào cho câu trả lời của Sam, vì vậy tôi chỉ có thể tưởng tượng mình đang phát điên. Tôi đã chỉnh sửa câu trả lời của mình để phản ánh điều đó.
Chủ nghĩa cá nhân

1
Haha, tất cả chúng ta đều có những khoảnh khắc như vậy phải không? Chúng tôi nhìn lại mã cũ và đi "tại sao tôi lại viết cái này?"
Joshua Walsh

1

Dựa trên các câu trả lời khác:

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
NƠI 1ST: Câu trả lời khó hiểu nhất không cần thiết. "Không cần thiết": Kích thước mã / lợi ích hiệu suất không đáng để chi phí dễ đọc. "Mật mã": Bản dịch quan trọng cần thiết để ánh xạ tới Yêu cầu "Layperson".
Tom

0

FWIW, đối với MS-SQL, biểu thức này sẽ thực hiện công việc. Giữ WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th') đầu tiên là danh sách đầu tiên trong danh sách, vì điều này phụ thuộc vào việc được thử trước các mục khác.

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

Đối với Excel:

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

Biểu thức (MOD(A1-11,100)>2)là TRUE (1) cho tất cả các số trừ mọi kết thúc bằng 11,12,13(FALSE = 0). Vì vậy, 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)kết thúc là 1 cho 11/12/13, nếu không:
1 đánh giá thành 3
2 đến 5,
3 đến 7
người khác: 9
- và 2 ký tự bắt buộc được chọn từ "thstndrdth"bắt đầu từ vị trí đó.

Nếu bạn thực sự muốn chuyển đổi khá trực tiếp sang SQL, thì điều này đã giúp tôi với một số giá trị thử nghiệm:

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

Đây là việc thực hiện dartvà có thể được sửa đổi theo ngôn ngữ.

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

Mặc dù có rất nhiều câu trả lời hay ở đây, tôi đoán có chỗ cho một câu trả lời khác, lần này dựa trên kết hợp mẫu, nếu không phải là bất cứ điều gì khác, thì ít nhất là cho khả năng đọc dễ tranh cãi

    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";
        }
    }

và điều gì làm cho giải pháp này đặc biệt? không có gì ngoài thực tế là tôi đang thêm một số cân nhắc về hiệu suất cho các giải pháp khác

thành thật mà nói tôi nghi ngờ hiệu suất thực sự quan trọng đối với kịch bản cụ thể này (người thực sự cần thứ tự hàng triệu số) nhưng ít nhất nó cũng đưa ra một số so sánh cần tính đến ...

1 triệu mặt hàng để tham khảo (khóa học của bạn có thể thay đổi dựa trên thông số kỹ thuật của máy)

với sự phù hợp và phân chia mẫu (câu trả lời này)

~ 622 ms

với khớp mẫu và chuỗi (câu trả lời này)

~ 1967 ms

với hai công tắc và bộ phận (câu trả lời được chấp nhận)

~ 637 ms

với một công tắc và bộ phận (câu trả lời khác)

~ 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

Một lớp lót khác, nhưng không có sự so sánh bằng cách chỉ lập kết quả regex thành một mảng.

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

Phiên bản PowerShell có thể được rút ngắn hơn nữa:

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

0

1 lớp lót khác.

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

-2

Đây là lớp Gia hạn DateTime. Sao chép, dán và thưởng thức

lớp tĩnh công khai 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;
    }
}

Kết quả :

Ngày 9 tháng 10 năm 2014


Bạn đang sao chép: a) Chuỗi định dạng ngày (X5) và b) toàn bộ phần còn lại của Phương thức (khi Trường hợp sử dụng có khả năng phát sinh (nếu chưa có) thì cần một hậu tố thứ tự cho ngày không phải là tháng mục đích hoặc thậm chí là một ngày trong tháng với một chuỗi định dạng ngày khác nhau). Sử dụng Phương pháp "OrdinalSuffix" mà tôi đề xuất được đưa ra từ câu trả lời của Ian Warburton vào ngày 6 tháng 4 năm 17 lúc 16:32 ( stackoverflow.com/questions/20156/ lỗi ).
Tom

-3

Một cách khác mà tôi đã sử dụng dựa trên tất cả các đề xuất khác, nhưng không yêu cầu vỏ đặc biệt:

    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";
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.