Відображення чисел із порядковим суфіксом у PHP


149

Я хочу відобразити цифри наступним чином

  • 1 як 1-й,
  • 2 як 2-й,
  • ...,
  • 150 як 150-й.

Як я повинен знайти правильний порядковий суфікс (st, nd, rd чи th) для кожного номера в моєму коді?


2
Дивіться мою відповідь тут. stackoverflow.com/questions/69262/… Це питання стосувалося .NET, але я відповів на вирішення PHP, тож він повинен вам допомогти.
nickf

Єдиний спосіб, з якого я можу це зробити, - це якщо оператор, що надходить на кожне число, яке ви могли б мати, IE, якщо (1), то "st" elseif (2), то "nd" і т. Д. І т. Д., Якщо (23000), то " nd ". Це проблема, якщо у вас є великі цифри, але ви можете написати програму для написання коду для вас, вона може циклічити всі числа, що друкують ifs для вас, щоб скопіювати + вставити у свій код.
Том Ґуллен

2
@Tom, таблиця пошуку може бути кращою, просто ініціалізуйте її зі значеннями 23000 і отримайте значення в індексі n, де n - число, яке ви хочете порядково.
Джон Бокер

2
Полковник Шрапнель. Ви можете геніально, але не всі. будь-який спосіб дякую, що проявили інтерес до мого запитання
Арк

@John, дуже розумна ідея, до неї буде дуже швидкий доступ, тому що кожен індекс представляє число, яке ви шукаєте.
Том Ґуллен

Відповіді:


283

з Вікіпедії :

$ends = array('th','st','nd','rd','th','th','th','th','th','th');
if (($number %100) >= 11 && ($number%100) <= 13)
   $abbreviation = $number. 'th';
else
   $abbreviation = $number. $ends[$number % 10];

Де $numberномер, який ви хочете написати. Працює з будь-яким натуральним числом.

Як функція:

function ordinal($number) {
    $ends = array('th','st','nd','rd','th','th','th','th','th','th');
    if ((($number % 100) >= 11) && (($number%100) <= 13))
        return $number. 'th';
    else
        return $number. $ends[$number % 10];
}
//Example Usage
echo ordinal(100);

Хоча спочатку трохи важко зрозуміти, але зараз я думаю, що це найкраще відображає, як ординальна система суфіксів працює для англійської мови.
erisco

6
Мені подобається ваше рішення. Крім того, якщо ви не хочете генерувати 0-ю модифікацію останнього рядка$abbreviation = ($number)? $number. $ends[$number % 10] : $number;
Гевін Джексон,

1
@GavinJackson ваше доповнення до цього чудового рішення дуже допомогло мені (+1). Не могли б ви пояснити мені, що відбувається в розрахунку? Я хочу зрозуміти. Ура! EDIT: Знайшов відповідь: умовний оператор
Ендрю Фокс

Вибачте, довелося перебити 111 голосів до 112: D Зробив це функцією в Delphi разом із демонстраційним додатком: pastebin.com/wvmz1CHY
Джеррі Додж

1
@HafezDivandari - ви впевнені? Щойно тестований з 7.1.19 і, здається, працює добре
Iacopo

142

PHP має вбудовану функціональність для цього . Він навіть справляється з інтернаціоналізацією!

$locale = 'en_US';
$nf = new NumberFormatter($locale, NumberFormatter::ORDINAL);
echo $nf->format($number);

Зауважте, що ця функція доступна лише в PHP 5.3.0 та новіших версіях.


15
Зауважте, для цього також потрібен PECL intl> = 1.0.0
rymo

4
Чи знаєте ви, чи можна отримати порядковий номер у формі слова? тобто перший, другий, третій і т.д. замість 1-го, 2-го, 3-го…
its_me

@jeremy Коли я намагався використовувати NumberFormatter, він завжди видає помилку NumberFomatter file not found. Як ви працювали над цим?
jhnferraris

1
@Jhn вам доведеться встановити розширенняapt-get install php5-intl
Aley

@Aley Я бачу. Yii має вбудований формат, який ми використовуємо зараз. хе
jhnferraris

20

Це може бути досягнуто в один рядок, використовуючи аналогічні функції у вбудованих функціях дати / часу PHP. Я смиренно подаю:

Рішення:

function ordinalSuffix( $n )
{
  return date('S',mktime(1,1,1,1,( (($n>=10)+($n>=20)+($n==0))*10 + $n%10) ));
}

Детальне пояснення:

Вбудована date()функція має суфіксну логіку для обробки обчислень n-го числа місяця. Суфікс повертається, коли Sвін задається у рядку формату:

date( 'S' , ? );

Так date()вимагає мітки часу (для ?вище), ми передамо наше ціле число в $nякості dayпараметра mktime()і використання фіктивних значень 1для hour, minute, secondі month:

date( 'S' , mktime( 1 , 1 , 1 , 1 , $n ) );

Це насправді виходить вишукано для значень поза діапазону для дня місяця (тобто $n > 31), але ми можемо додати кілька простих вбудованих логік для обмеження $nна 29:

date( 'S', mktime( 1, 1, 1, 1, ( (($n>=10)+($n>=20))*10 + $n%10) ));

Єдине позитивне значення( Травень 2017 року ) це не вдається $n == 0, але це легко виправити, додавши 10 у цьому спеціальному випадку:

date( 'S', mktime( 1, 1, 1, 1, ( (($n>=10)+($n>=20)+($n==0))*10 + $n%10) ));

Оновлення, травень 2017 року

Як зауважив @donatJ, вищесказане не відповідає вище 100 (наприклад, "111st"), оскільки >=20перевірки завжди повертаються як істинні. Щоб скинути їх на кожне століття, ми додаємо фільтр до порівняння:

date( 'S', mktime( 1, 1, 1, 1, ( (($n>=10)+($n%100>=20)+($n==0))*10 + $n%10) ));

Просто загорніть його у функцію для зручності, і ви не їдете!


14

Ось один вкладиш:

$a = <yournumber>;
echo $a.substr(date('jS', mktime(0,0,0,1,($a%10==0?9:($a%100>20?$a%10:$a%100)),2000)),-2);

Мабуть, найкоротше рішення. Можна, звичайно, перетворити функцію:

function ordinal($a) {
  // return English ordinal number
  return $a.substr(date('jS', mktime(0,0,0,1,($a%10==0?9:($a%100>20?$a%10:$a%100)),2000)),-2);
}

З повагою, Пол

EDIT1: Виправлення коду для 11 по 13.

EDIT2: Виправлення коду для 111, 211, ...

EDIT3: Тепер він працює правильно також для кратних 10.


Мені подобається такий підхід, але, на жаль, він не працює :-( 30-е виходить, як 30-е, 40-е виходить як 40-е. І т.д.
Flukey

1
Так, вибачте. Коли я читав запитання, яке я думав, то, так, це повинно бути можливим за допомогою одного рядка коду. І я просто набрав його. Як ви бачите з моїх змін, я вдосконалююсь. Після третьої редакції зараз я думаю, що це повністю зроблено. Принаймні всі цифри від 1 до 150 добре друкуються на моєму екрані.
Пол

Добре виглядає до 500! (не перевіряли його далі). Гарна робота! :-)
Flukey

13

з http://www.phpro.org/examples/Ordinal-Suffix.html

<?php

/**
 *
 * @return number with ordinal suffix
 *
 * @param int $number
 *
 * @param int $ss Turn super script on/off
 *
 * @return string
 *
 */
function ordinalSuffix($number, $ss=0)
{

    /*** check for 11, 12, 13 ***/
    if ($number % 100 > 10 && $number %100 < 14)
    {
        $os = 'th';
    }
    /*** check if number is zero ***/
    elseif($number == 0)
    {
        $os = '';
    }
    else
    {
        /*** get the last digit ***/
        $last = substr($number, -1, 1);

        switch($last)
        {
            case "1":
            $os = 'st';
            break;

            case "2":
            $os = 'nd';
            break;

            case "3":
            $os = 'rd';
            break;

            default:
            $os = 'th';
        }
    }

    /*** add super script ***/
    $os = $ss==0 ? $os : '<sup>'.$os.'</sup>';

    /*** return ***/
    return $number.$os;
}
?> 

9

Простий і легкий відповідь буде:

$Day = 3; 
echo date("S", mktime(0, 0, 0, 0, $Day, 0));

//OUTPUT - rd

Нічого не сказати про дати у запитанні Тхо. Я все-таки підтримав рішення - це швидке і просте рішення, коли ви знаєте, що діапазон номерів буде <32
cronoklee

8

Я написав це для PHP4. Це працює добре, і це досить економно.

function getOrdinalSuffix($number) {
    $number = abs($number) % 100;
    $lastChar = substr($number, -1, 1);
    switch ($lastChar) {
        case '1' : return ($number == '11') ? 'th' : 'st';
        case '2' : return ($number == '12') ? 'th' : 'nd';
        case '3' : return ($number == '13') ? 'th' : 'rd'; 
    }
    return 'th';  
}

Вішале Кумаре, ви можете розширити / пояснити / пояснити це трохи більше? Ткс!
iletras

4

вам просто потрібно застосувати задану функцію.

function addOrdinalNumberSuffix($num) {
  if (!in_array(($num % 100),array(11,12,13))){
    switch ($num % 10) {
      // Handle 1st, 2nd, 3rd
      case 1:  return $num.'st';
      case 2:  return $num.'nd';
      case 3:  return $num.'rd';
    }
  }
  return $num.'th';
}

3

Загалом, ви можете використовувати це і викликати echo get_placing_string (100);

<?php
function get_placing_string($placing){
    $i=intval($placing%10);
    $place=substr($placing,-2); //For 11,12,13 places

    if($i==1 && $place!='11'){
        return $placing.'st';
    }
    else if($i==2 && $place!='12'){
        return $placing.'nd';
    }

    else if($i==3 && $place!='13'){
        return $placing.'rd';
    }
    return $placing.'th';
}
?>

2

Я зробив функцію, яка не покладається на функцію PHP, date();оскільки вона не потрібна, але також зробила її такою компактною і короткою, наскільки я думаю, що це можливо.

Код : (всього 121 байт)

function ordinal($i) { // PHP 5.2 and later
  return($i.(($j=abs($i)%100)>10&&$j<14?'th':(($j%=10)>0&&$j<4?['st', 'nd', 'rd'][$j-1]:'th')));
}

Більш компактний код нижче.

Він працює наступним чином :

printf("The %s hour.\n",    ordinal(0));   // The 0th hour.
printf("The %s ossicle.\n", ordinal(1));   // The 1st ossicle.
printf("The %s cat.\n",     ordinal(12));  // The 12th cat.
printf("The %s item.\n",    ordinal(-23)); // The -23rd item.

Що потрібно знати про цю функцію :

  • Він має справу з негативними цілими числами, такими ж, як і натуральні числа, і зберігає знак.
  • Він повертає 11-е, 12-е, 13-е, 811-е, 812-е, 813-е і т.д. для -надцяти чисел, як очікувалося.
  • Він не перевіряє десяткові, але залишити їх на місці (використання floor($i), round($i)або ceil($i)на початку заключного повернення заяви).
  • Ви також можете додати format_number($i)на початку остаточного оператора return, щоб отримати відокремлене комою ціле число (якщо відображаються тисячі, мільйони тощо).
  • Ви можете просто видалити $iз початку зворотного оператора, якщо ви хочете повернути порядковий суфікс без того, що ви ввели.

Ця функція працює починаючи з PHP 5.2, випущеного в листопаді 2006 року, виключно через синтаксис короткого масиву. Якщо у вас є версія до цього, то будь ласка, оновіть, тому що ви майже застаріли десятиліття! Якщо цього не зробити, просто замініть рядок ['st', 'nd', 'rd']тимчасовою змінною, що містить array('st', 'nd', 'rd');.

Ця ж функція (без повернення вводу), але розкритий вигляд моєї короткої функції для кращого розуміння:

function ordinal($i) {
  $j = abs($i); // make negatives into positives
  $j = $j%100; // modulo 100; deal only with ones and tens; 0 through 99

  if($j>10 && $j<14) // if $j is over 10, but below 14 (so we deal with 11 to 13)
    return('th'); // always return 'th' for 11th, 13th, 62912th, etc.

  $j = $j%10; // modulo 10; deal only with ones; 0 through 9

  if($j==1) // 1st, 21st, 31st, 971st
    return('st');

  if($j==2) // 2nd, 22nd, 32nd, 582nd
    return('nd'); // 

  if($j==3) // 3rd, 23rd, 33rd, 253rd
    return('rd');

  return('th'); // everything else will suffixed with 'th' including 0th
}

Оновлення коду :

Ось модифікована версія, яка на 14 цілих байт коротша (всього 107 байт):

function ordinal($i) {
  return $i.(($j=abs($i)%100)>10&&$j<14?'th':@['th','st','nd','rd'][$j%10]?:'th');
}

Або якомога коротше, на 25 байт коротше (всього 96 байт):

function o($i){return $i.(($j=abs($i)%100)>10&&$j<14?'th':@['th','st','nd','rd'][$j%10]?:'th');}

З цією останньою функцією просто зателефонуйте, o(121);і вона зробить точно так само, як і інші функції, які я перерахував.

Оновлення коду №2 :

Ми з Бен працювали разом і скоротили її на 38 байт (всього 83 байти):

function o($i){return$i.@(($j=abs($i)%100)>10&&$j<14?th:[th,st,nd,rd][$j%10]?:th);}

Ми не вважаємо, що це може бути коротшим за це! Однак ми хочемо бути неправдивими. :)

Сподіваюся, вам всі сподобаються.


вам не потрібно використовувати abs()модуль%
99 проблем - синтаксис не один

Покладаючись лише на модуль, все одно залишать негативний знак на місці. abs();прибирає негативний знак, який саме мені потрібен.
nxasdf

1

Ще коротша версія для дат у місяці (до 31) замість того, щоб використовувати mktime () і не вимагати pecl intl:

function ordinal($n) {
    return (new DateTime('Jan '.$n))->format('jS');
}

або процедурно:

echo date_format(date_create('Jan '.$n), 'jS');

Це, звичайно, працює, тому що місяць, який я вибрав за замовчуванням (січень), має 31 день.

Цікаво, що якщо ви спробуєте це з лютого (або іншого місяця без 31 дня), він перезапуститься до кінця:

...clip...
31st
1st
2nd
3rd

тож ви можете рахувати до днів цього місяця із зазначенням дати tу циклі: кількість днів у місяці.


1
function ordinal($number){

    $last=substr($number,-1);
    if( $last>3 || $last==0 || ( $number >= 11 && $number <= 19 ) ){
      $ext='th';
    }else if( $last==3 ){
      $ext='rd';
    }else if( $last==2 ){
      $ext='nd';
    }else{
      $ext='st';
    }
    return $number.$ext;
  }

0

Знайшов відповідь на PHP.net

<?php
function ordinal($num)
{
    // Special case "teenth"
    if ( ($num / 10) % 10 != 1 )
    {
        // Handle 1st, 2nd, 3rd
        switch( $num % 10 )
        {
            case 1: return $num . 'st';
            case 2: return $num . 'nd';
            case 3: return $num . 'rd';  
        }
    }
    // Everything else is "nth"
    return $num . 'th';
}
?>

0

Ось ще одна дуже коротка версія з використанням функцій дати. Він працює для будь-якої кількості (не обмежується днями місяця) і враховує, що * 11-й * 12-й * 13-й не відповідають формату * 1-й * 2-й * 3-й.

function getOrdinal($n)
{
    return $n . date_format(date_create('Jan ' . ($n % 100 < 20 ? $n % 20 : $n % 10)), 'S');
}

-1

Мені подобається цей маленький фрагмент

<?php

  function addOrdinalNumberSuffix($num) {
    if (!in_array(($num % 100),array(11,12,13))){
      switch ($num % 10) {
        // Handle 1st, 2nd, 3rd
        case 1:  return $num.'st';
        case 2:  return $num.'nd';
        case 3:  return $num.'rd';
      }
    }
    return $num.'th';
  }

?>

ТУТ


1
Я не зовсім впевнений, що пропонує ця відповідь на додаток до відповіді ChintanThummar. Ну, натякає, що ChintanThummar допустив порушення авторських прав, якщо тільки він не написав код у вашого джерела ...
Andreas Rejbrand
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.