Wyświetla liczby z przyrostkiem porządkowym w PHP

149

Chcę wyświetlić liczby w następujący sposób

  • 1 jako 1,
  • 2 jako 2,
  • ...,
  • 150 jako 150.

Jak znaleźć poprawny przyrostek porządkowy (st, nd, rd lub th) dla każdej liczby w moim kodzie?

Arka
źródło
2
Zobacz moją odpowiedź tutaj. stackoverflow.com/questions/69262/… To pytanie dotyczyło .NET, ale odpowiedziałem, podając rozwiązanie PHP, więc powinno ci pomóc.
nickf
Jedynym sposobem, w jaki mogę o tym pomyśleć, jest użycie instrukcji if dla każdej możliwej liczby, IE, if (1), a następnie "st" elseif (2), następnie "nd" itp. Itd. If (23000) then " nd ”. Jest to problem, jeśli masz duże liczby, ale możesz napisać program, który napisze kod za Ciebie, może zapętlić wszystkie liczby, wypisując ifs, abyś mógł skopiować i wkleić do kodu.
Tom Gullen
2
@Tom, tabela przeglądowa może być lepsza, po prostu zainicjuj ją 23000 wartościami i uzyskaj wartość o indeksie n, gdzie n to liczba, która ma być liczbą porządkową.
John Boker,
2
Pułkownik Shrapnel. możesz genialny, ale nie wszyscy. w każdym razie dziękuję za zainteresowanie moim pytaniem
ArK
@John, bardzo sprytny pomysł, dostęp do niego byłby bardzo szybki, ponieważ każdy indeks reprezentuje numer, który szukasz.
Tom Gullen,

Odpowiedzi:

283

z wikipedii :

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

Gdzie $numberjest numer, który chcesz wpisać. Działa z dowolną liczbą naturalną.

Jako funkcja:

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);
Iacopo
źródło
Chociaż na początku trochę trudne do zrozumienia, myślę, że teraz najlepiej oddaje sposób działania systemu przyrostków porządkowych w języku angielskim.
erisco
6
Podoba mi się twoje rozwiązanie. Dodatkowo, jeśli wolisz nie generować 0, zmień ostatnią linię na$abbreviation = ($number)? $number. $ends[$number % 10] : $number;
Gavin Jackson
1
@GavinJackson Twój dodatek do tego doskonałego rozwiązania naprawdę mi pomógł (+1). Czy mógłbyś mi wyjaśnić, co się dzieje w obliczeniach? Chcę zrozumieć. Twoje zdrowie! EDYCJA: Znaleziono odpowiedź: operator warunkowy
Andrew Fox
Przepraszamy, musiałem złamać 111 głosów na 112: D Uruchomiono go w Delphi wraz z aplikacją demonstracyjną: pastebin.com/wvmz1CHY
Jerry Dodge
1
@HafezDivandari - czy na pewno? Właśnie przetestowany z 7.1.19 i wydaje się, że działa dobrze
Iacopo,
142

PHP ma do tego wbudowaną funkcjonalność . Obsługuje nawet internacjonalizację!

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

Zauważ, że ta funkcja jest dostępna tylko w PHP 5.3.0 i nowszych.

Jeremy Kauffman
źródło
15
Zauważ, że wymaga to również PECL intl> = 1.0.0
rymo
4
Czy wiesz, czy można uzyskać liczbę porządkową w formie słowa? tj. pierwszy, drugi, trzeci itd. zamiast 1, 2, 3…
its_me
@jeremy Kiedy próbowałem użyć NumberFormatter, zawsze generuje błąd NumberFomatter file not found. Jak sobie z tym poradziłeś?
jhnferraris,
1
@Jhn musisz zainstalować rozszerzenieapt-get install php5-intl
Aley
@Aley, widzę. Yii ma wbudowany program formatujący, którego teraz używamy. heh
jhnferraris
20

Można to osiągnąć w jednym wierszu, wykorzystując podobną funkcjonalność wbudowanych funkcji daty / czasu PHP. Pokornie poddaję się:

Rozwiązanie:

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

Szczegółowe wyjaśnienie:

Wbudowana date()funkcja ma logikę sufiksu do obsługi obliczeń n-tego dnia miesiąca. Sufiks jest zwracany, gdy Sjest podany w ciągu formatu:

date( 'S' , ? );

Ponieważ date()wymaga znacznika czasu (dla ?powyżej), będziemy przechodzić naszą całkowitą $njako dayparametr mktime()i wykorzystania manekina wartości 1dla hour, minute, second, i month:

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

To faktycznie kończy się niepowodzeniem w przypadku wartości spoza zakresu dla dnia miesiąca (tj. $n > 31), Ale możemy dodać prostą logikę wbudowaną do ograniczenia $ndo 29:

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

Jedyna wartość dodatnia( Maj 2017 ) to nie działa $n == 0, ale można to łatwo naprawić, dodając 10 w tym specjalnym przypadku:

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

Aktualizacja, maj 2017

Jak zauważył @donatJ, powyższe zawodzi powyżej 100 (np. „111st”), ponieważ >=20sprawdzenia zawsze zwracają prawdę. Aby zresetować je co stulecie, dodajemy filtr do porównania:

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

Po prostu zapakuj go w funkcję dla wygody i gotowe!

Andrzeja Kozaka
źródło
14

Oto jedna linijka:

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

Prawdopodobnie najkrótsze rozwiązanie. Oczywiście można ją opakować funkcją:

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

Pozdrawiam, Paul

EDIT1: Korekta kodu od 11 do 13.

EDIT2: Korekta kodu dla 111, 211, ...

EDIT3: Teraz działa poprawnie również dla wielokrotności 10.

Paweł
źródło
Podoba mi się to podejście, ale niestety nie działa :-( 30 miejsce wychodzi jako 30. 40. wychodzi jako 40. itd.
Flukey
1
Tak, przepraszam. Kiedy przeczytałem pytanie, które pomyślałem, hej, powinno to być możliwe za pomocą jednej linii kodu. I właśnie go przepisałem. Jak widać po moich zmianach, poprawiam się. Po trzeciej edycji wydaje mi się, że jest już gotowy. Przynajmniej wszystkie liczby od 1 do 150 ładnie wypisują się na moim ekranie.
Paul
Wygląda dobrze do 500! (nie testowałem tego dalej). Dobra robota! :-)
Flukey
13

z 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;
}
?> 
John Boker
źródło
9

Prosta i łatwa odpowiedź brzmi:

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

//OUTPUT - rd
Biały koń
źródło
Jednak nie mówi nic o datach w pytaniu. I upvoted zresztą - to szybkie i proste rozwiązanie, gdy wiesz, że zakres liczba będzie <32
cronoklee
8

Napisałem to dla PHP4. Działa dobrze i jest dość ekonomiczne.

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
źródło
Vishal Kumar, czy mógłbyś to nieco rozszerzyć / objaśnić / wyjaśnić? Tks!
iletras
4

wystarczy zastosować daną funkcję.

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';
}
Chintan Thummar
źródło
3

Generalnie możesz tego użyć i wywołać 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';
}
?>
Uzair Bin Nisar
źródło
2

Zrobiłem funkcję, która nie opiera się na funkcji PHP, date();ponieważ nie jest to konieczne, ale także uczyniłem ją tak zwartą i tak krótką, jak myślę, że jest obecnie możliwa.

Kod : (łącznie 121 bajtów)

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

Bardziej zwarty kod poniżej.

Działa w następujący sposób :

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.

Co warto wiedzieć o tej funkcji :

  • Zajmuje się ujemnymi liczbami całkowitymi tak samo jak dodatnimi liczbami całkowitymi i zachowuje znak.
  • Zwraca 11, 12, 13, 811, 812, 813 itd. Dla -naście liczb zgodnie z oczekiwaniami.
  • To nie sprawdza dziesiętne, ale zostawię je na miejscu (stosowanie floor($i), round($i)lub ceil($i)na początku końcowego instrukcji return).
  • Możesz również dodać format_number($i)na początku ostatniej instrukcji return, aby uzyskać liczbę całkowitą oddzieloną przecinkami (jeśli wyświetlasz tysiące, miliony itp.).
  • Możesz po prostu usunąć $iz początku instrukcji return, jeśli chcesz zwrócić tylko przyrostek porządkowy bez tego, co wprowadzisz.

Ta funkcja działa od wersji PHP 5.2 wydanej w listopadzie 2006 wyłącznie z powodu składni krótkich tablic. Jeśli masz wersję wcześniejszą, zaktualizuj ją, ponieważ jesteś prawie dziesięć lat nieaktualny! Jeśli to się nie uda, po prostu zamień in-line ['st', 'nd', 'rd']na tymczasową zmienną zawierającą array('st', 'nd', 'rd');.

Ta sama funkcja (bez zwracania danych wejściowych), ale rozstrzelony widok mojej krótkiej funkcji dla lepszego zrozumienia:

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
}

Aktualizacja kodu :

Oto zmodyfikowana wersja, która jest krótsza o 14 pełnych bajtów (łącznie 107 bajtów):

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

Lub tak krótko, jak to możliwe, będąc o 25 bajtów krótszym (łącznie 96 bajtów):

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

Dzięki tej ostatniej funkcji po prostu wywołaj, o(121);a zrobi dokładnie to samo, co inne funkcje, które wymieniłem.

Aktualizacja kodu nr 2 :

Ben i ja pracowaliśmy razem i zmniejszyliśmy to o 38 bajtów (łącznie 83 bajty):

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

Nie sądzimy, że może to być krótsze niż to! Chcąc jednak udowodnić, że się mylisz. :)

Mam nadzieję, że wszystkim się spodoba.

nxasdf
źródło
nie musisz używać abs()z modułem%
99 problemów - Składnia to nie jeden
Poleganie tylko na modulo nadal pozostawi znak ujemny na miejscu. abs();usuwa znak minus, którego potrzebowałem.
nxasdf,
1

Jeszcze krótsza wersja dla dat w miesiącu (do 31) zamiast używania mktime () i niewymagająca pecl intl:

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

lub proceduralnie:

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

Działa to oczywiście, ponieważ domyślny miesiąc, który wybrałem (styczeń), ma 31 dni.

Co ciekawe, jeśli spróbujesz go z lutym (lub kolejnym miesiącem bez 31 dni), uruchomi się ponownie przed końcem:

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

więc możesz liczyć do dni tego miesiąca ze specyfikatorem daty tw pętli: liczba dni w miesiącu.

Dan Dart
źródło
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;
  }
xyz
źródło
0

Znalazłem odpowiedź w 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';
}
?>
mysticmo
źródło
0

Oto kolejna bardzo krótka wersja korzystająca z funkcji daty. Działa dla dowolnej liczby (nie jest ograniczona liczbą dni miesiąca) i bierze pod uwagę, że * 11 * 12 * 13 nie ma formatu * 1 * 2 * 3.

function getOrdinal($n)
{
    return $n . date_format(date_create('Jan ' . ($n % 100 < 20 ? $n % 20 : $n % 10)), 'S');
}
Claire Matthews
źródło
-1

Podoba mi się ten mały fragment

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

?>

TUTAJ

niksmac
źródło
1
Nie jestem pewien, co oferuje ta odpowiedź oprócz odpowiedzi Chintana Thummara. Cóż, sugeruje, że Chintan Thummar naruszył prawa autorskie, chyba że napisał kod u twojego źródła ...
Andreas Rejbrand