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.