Есть ли простой способ создания ординалов в C #? - PullRequest
188 голосов
/ 21 августа 2008

Есть ли в C # простой способ создания ординалов для числа? Например:

  • 1 возвращает 1-е
  • 2 возвращает 2-е
  • 3 возвращает 3-е
  • ... и т.д.

Можно ли это сделать с помощью String.Format() или для этого есть какие-либо функции?

Ответы [ 17 ]

287 голосов
/ 21 августа 2008

На этой странице представлен полный список всех пользовательских правил числового форматирования:

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

Как видите, там нет ничего об ординалах, так что это нельзя сделать с помощью String.Format. Однако не так уж сложно написать функцию для этого.

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 (). </p>

Также обратите внимание, что это не интернационализировано. Я не знаю, как выглядят ординалы на других языках.

69 голосов
/ 22 сентября 2008

Помните интернационализацию!

Решения здесь работают только для английского языка. Все становится намного сложнее, если вам нужно поддерживать другие языки.

Например, на испанском языке «1-й» будет записываться как «1.o», «1.a», «1.os» или «1.as» в зависимости от того, является ли вещь, которую вы рассчитываете, мужской, женский или множественное число!

Поэтому, если ваше программное обеспечение должно поддерживать разные языки, старайтесь избегать порядковых чисел.

21 голосов
/ 07 марта 2009

Моя версия версий Джесси Стю и Самджудсона:)

Включен модульный тест, показывающий, что принятый ответ неверен, когда число <1 </p>

    /// <summary>
    /// Get the ordinal value of positive integers.
    /// </summary>
    /// <remarks>
    /// Only works for english-based cultures.
    /// Code from: /28553/est-li-prostoi-sposob-sozdaniya-ordinalov-v-c-##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());
    }
12 голосов
/ 27 августа 2008

Мне скорее понравились элементы из решений 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;
        }
    }
12 голосов
/ 21 августа 2008

Тебе придётся катиться самостоятельно. С макушки головы:

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. Я сказал от макушки головы: -)

Отредактировано для 1011 - другие уже исправили это, просто хотят убедиться, что другие не получат эту неправильную версию.

11 голосов
/ 18 октября 2014

Простой, чистый, быстрый

    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();
8 голосов
/ 21 сентября 2008

Хотя я еще не тестировал этот показатель, вы сможете добиться лучшей производительности, избегая всех условных операторов case.

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

Обратите внимание, сокращение условных выражений и использование поиска в массиве должно ускорить производительность, если генерировать много ординалов в узком цикле. Тем не менее, я также признаю, что это не так легко читается, как решение с кейсом.

3 голосов
/ 02 мая 2013

Аналогично решению Райана, но даже более простому, я просто использую простой массив и использую день, чтобы найти правильный порядковый номер:

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 голосов
/ 23 сентября 2016

Я использую этот класс расширения:

public static class Int32Extensions
{
    public static string ToOrdinal(this int i)
    {
        return (i + "th")
            .Replace("1th", "1st")
            .Replace("2th", "2nd")
            .Replace("3th", "3rd");
    }
}
2 голосов
/ 27 апреля 2016
        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

...