Существует ли простой способ создать ординалы в C#?

196
задан Marek Grzenkowicz 26 September 2011 в 02:04
поделиться

8 ответов

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

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

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

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 () методы.

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

302
ответ дан staafl 23 November 2019 в 05:18
поделиться

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

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

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

8
ответ дан Ryan McGeary 23 November 2019 в 05:18
поделиться

Вы будете иметь к самокрутке. От вершины моей головы:

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

Отредактированный для 12.11.13 исключений. Я, которого DID говорит от вершины моего главного:-)

Отредактированный для 1 011 - другие уже зафиксировали это, просто хотят удостовериться, что другие не захватывают эту неверную версию.

13
ответ дан Stu 23 November 2019 в 05:18
поделиться

Мне скорее понравились элементы и от 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
ответ дан Community 23 November 2019 в 05:18
поделиться

Моя версия версии Jesse версий Stu и samjudson:)

Включенный модульный тест, чтобы показать, что принятый ответ является неправильным когда число < 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());
    }
21
ответ дан si618 23 November 2019 в 05:18
поделиться

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

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

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

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

72
ответ дан roomaroo 23 November 2019 в 05:18
поделиться

Другая острота, но без сравнений, только индексируя 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
ответ дан 23 November 2019 в 05:18
поделиться

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

    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 миллион объектов для ссылки (Ваш millage может варьироваться на основе спецификаций машины, конечно)

с сопоставлением с образцом и подразделениями (этот ответ)

~622 мс

с сопоставлением с образцом и строками (этот ответ)

~1967 мс

с двумя переключателями и подразделениями (принятый ответ)

~637 мс

с одним переключателем и подразделениями (другой ответ)

~725 мс

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
ответ дан 23 November 2019 в 05:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: