Visualizza i numeri con suffisso ordinale in PHP


149

Voglio visualizzare i numeri come segue

  • 1 come 1o,
  • 2 come secondo,
  • ...,
  • 150 come 150th.

Come devo trovare il suffisso ordinale corretto (st, nd, rd o th) per ciascun numero nel mio codice?


2
Vedi la mia risposta qui. stackoverflow.com/questions/69262/… Quella domanda era per .NET, ma ho risposto con una soluzione PHP, quindi dovrebbe aiutarti.
nickf

L'unico modo in cui posso pensare di farlo è avere un'istruzione if in aumento per ogni numero che potresti avere, IE, se (1) quindi "st" elseif (2) quindi "nd" ecc ecc if (23000) then " nd". È un problema se hai grandi numeri ma potresti scrivere un programma per scrivere il codice per te, potrebbe fare un ciclo di tutti i numeri che stampano gli if per farti copiare + incollare nel tuo codice.
Tom Gullen,

2
@Tom, una tabella di ricerca potrebbe essere migliore, basta inizializzarla con i 23000 valori e ottenere il valore nell'indice n, dove n è il numero desiderato per l'ordinale.
John Boker,

2
Col. Shrapnel. potresti essere brillante ma non tutto. comunque grazie per aver mostrato interesse per la mia domanda
ArK

@Giovanni, idea molto intelligente, sarebbe molto veloce accedere così come ogni indice rappresenta il numero che stai cercando.
Tom Gullen,

Risposte:


283

da Wikipedia :

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

Dov'è $numberil numero che vuoi scrivere. Funziona con qualsiasi numero naturale.

Come una funzione:

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

Sebbene all'inizio sia un po 'difficile da capire, ora penso che rappresenti al meglio il modo in cui il sistema di suffisso ordinale funziona per l'inglese.
erisco,

6
Mi piace la tua soluzione. Inoltre, se si preferisce non generare l' ennesima modifica, l'ultima riga deve essere$abbreviation = ($number)? $number. $ends[$number % 10] : $number;
Gavin Jackson,

1
@GavinJackson la tua aggiunta a questa eccellente soluzione mi ha davvero aiutato (+1). Potresti spiegarmi cosa sta succedendo nel calcolo? Voglio capire. Saluti! EDIT: Trovato una risposta: operatore condizionale
Andrew Fox

Siamo spiacenti, ho dovuto rompere i 111 voti a 112: D Ha reso una funzione in Delphi insieme a un'app demo: pastebin.com/wvmz1CHY
Jerry Dodge,

1
@HafezDivandari - sei sicuro? Appena testato con 7.1.19 e sembra funzionare bene
Iacopo il

142

PHP ha funzionalità integrate per questo . Gestisce persino l'internazionalizzazione!

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

Nota che questa funzionalità è disponibile solo in PHP 5.3.0 e versioni successive.


15
Nota che questo richiede anche PECL intl> = 1.0.0
rymo

4
Sai se è possibile ottenere il numero ordinale in forma di parola? cioè primo, secondo, terzo, ecc. invece di 1 °, 2 °, 3 ° ...
its_me

@jeremy Quando ho provato ad usare NumberFormatter, viene sempre generato un errore NumberFomatter file not found. Come hai lavorato su questo?
jhnferraris,

1
@Jhn devi installare l'estensioneapt-get install php5-intl
Aley

@Aley vedo. Yii ha un formatter incorporato che stiamo usando in questo momento. heh
jhnferraris,

20

Ciò può essere realizzato in una sola riga sfruttando funzionalità simili nelle funzioni di data / ora incorporate di PHP. Umilmente invio:

Soluzione:

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

Spiegazione dettagliata:

La date()funzione integrata ha una logica di suffisso per la gestione dei calcoli dell'ennesimo giorno del mese. Il suffisso viene restituito quando Sviene fornito nella stringa di formato:

date( 'S' , ? );

Dal momento che date()richiede un timestamp (per ?sopra), passeremo il nostro intero $ncome dayparametro mktime()e l'uso fittizio valori 1per il hour, minute, second, e month:

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

Questo in realtà fallisce con garbo sui valori fuori range per un giorno del mese (cioè $n > 31) ma possiamo aggiungere una semplice logica inline per limitare $na 29:

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

L'unico valore positivo( Maggio 2017 ) non funziona $n == 0, ma è stato risolto facilmente aggiungendo 10 in quel caso speciale:

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

Aggiornamento, maggio 2017

Come osservato da @donatJ, quanto sopra fallisce al di sopra di 100 (ad es. "111st"), poiché i >=20controlli ritornano sempre veri. Per resettarli ogni secolo, aggiungiamo un filtro al confronto:

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

Basta avvolgerlo in una funzione per comodità e il gioco è fatto!


14

Ecco un one-liner:

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

Probabilmente la soluzione più breve. Naturalmente può essere avvolto da una funzione:

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

Saluti, Paul

EDIT1: correzione del codice da 11 a 13.

EDIT2: correzione del codice per 111, 211, ...

EDIT3: ora funziona correttamente anche per multipli di 10.


Mi piace questo approccio, ma purtroppo non funziona :-( il 30esimo esce come 30esimo. 40esimo esce come 40esimo ecc.
Flukey

1
Si scusa. Quando leggo la domanda che ho pensato, ehi, questo dovrebbe essere possibile con una singola riga di codice. E l'ho appena digitato. Come vedi dalle mie modifiche, sto migliorando. Dopo la terza modifica ora penso che sia abbastanza fatto. Almeno tutti i numeri da 1 a 150 vengono stampati bene sul mio schermo.
Paul,

Sembra buono fino a 500! (non l'ho testato oltre). Buon lavoro! :-)
Flukey,

13

da 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

La risposta semplice e facile sarà:

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

//OUTPUT - rd

Non dice nulla riguardo alle date nella domanda. Ho comunque votato - è una soluzione semplice e veloce quando sai che l'intervallo di numeri sarà <32
cronoklee

8

Ho scritto questo per PHP4. Funziona bene ed è piuttosto economico.

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

Vishal Kumar, potresti espandere / esporre / spiegarlo un po 'di più? Tks!
iletras,

4

devi solo applicare una determinata funzione.

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

Generalmente, puoi usarlo e chiamare 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

Ho creato una funzione che non si basa sulla date();funzione del PHP in quanto non è necessaria, ma l'ha anche resa compatta e breve come penso sia attualmente possibile.

Il codice : (121 byte in totale)

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

Codice più compatto di seguito.

Funziona come segue :

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.

Cose da sapere su questa funzione :

  • Tratta di numeri interi negativi come numeri interi positivi e mantiene il segno.
  • Restituisce 11 °, 12 °, 13 °, 811 °, 812 °, 813 °, ecc. Per i sedici numeri come previsto.
  • Esso non controlla decimali, ma li lascerà al suo posto (uso floor($i), round($i)o ceil($i)all'inizio della dichiarazione finale di ritorno).
  • Puoi anche aggiungere format_number($i)all'inizio dell'istruzione return finale per ottenere un numero intero separato da virgole (se stai visualizzando migliaia, milioni, ecc.).
  • Puoi semplicemente rimuovere $idall'inizio dell'istruzione return se vuoi solo restituire il suffisso ordinale senza quello che hai inserito.

Questa funzione è operativa a partire da PHP 5.2 rilasciato nel novembre 2006 a causa della breve sintassi dell'array. Se hai una versione precedente, esegui l'upgrade perché sei quasi obsoleto da un decennio! In caso contrario, basta sostituire l'in-line ['st', 'nd', 'rd']con una variabile temporanea contenente array('st', 'nd', 'rd');.

La stessa funzione (senza restituire l'input), ma una vista esplosa della mia breve funzione per una migliore comprensione:

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
}

Aggiornamento del codice :

Ecco una versione modificata che è più corta di 14 byte interi (107 byte in totale):

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

O il più breve possibile di 25 byte in meno (96 byte in totale):

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

Con quest'ultima funzione, basta chiamare o(121);e farà esattamente lo stesso delle altre funzioni che ho elencato.

Aggiornamento codice n. 2 :

Ben ed io abbiamo lavorato insieme e ridotto di 38 byte (83 byte in totale):

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

Non pensiamo che possa essere più breve di così! Volendo essere smentito, tuttavia. :)

Spero che vi divertiate tutti.


non è necessario utilizzarlo abs()con il modulo%
99 Problemi - La sintassi non è un

Affidarsi solo al modulo lascerà comunque in posizione il segno negativo. abs();rimuove il segno negativo che è quello di cui avevo bisogno.
nxasdf,

1

Una versione ancora più breve per le date del mese (fino a 31) invece di usare mktime () e non richiede pecl intl:

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

o proceduralmente:

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

Questo funziona ovviamente perché il mese predefinito che ho scelto (gennaio) ha 31 giorni.

È interessante notare che se lo provi con febbraio (o un altro mese senza 31 giorni), si riavvia prima della fine:

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

quindi puoi contare fino ai giorni di questo mese con l'identificatore di data tnel tuo ciclo: numero di giorni del mese.


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

Trovato una risposta in 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

Ecco un'altra versione molto breve che utilizza le funzioni di data. Funziona per qualsiasi numero (non vincolato dai giorni del mese) e tiene conto del fatto che * 11 * * 12 * * 13 non segue il formato * 1 * 2 * * 3.

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

-1

Adoro questo piccolo frammento

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

?>

QUI


1
Non sono sicuro di cosa offra questa risposta oltre alla risposta di ChintanTumum. Bene, suggerisce che Chintan Thummar ha commesso una violazione del copyright, a meno che non abbia scritto il codice alla tua fonte ...
Andreas Rejbrand,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.