Mostrar números con sufijo ordinal en PHP

149

Quiero mostrar los números de la siguiente manera

  • 1 como 1er,
  • 2 como 2do,
  • ...
  • 150 como 150

¿Cómo debo encontrar el sufijo ordinal correcto (st, nd, rd o th) para cada número en mi código?

Arca
fuente
2
Mira mi respuesta aquí. stackoverflow.com/questions/69262/… Esa pregunta era para .NET, pero respondí con una solución PHP, por lo que debería ayudarte.
nickf
La única forma en que puedo pensar en hacer esto es tener una declaración if para cada número que pueda tener, es decir, if (1) luego "st" elseif (2) luego "nd", etc., etc. if (23000) luego " Dakota del Norte". Es un problema si tiene números grandes, pero podría escribir un programa para escribir el código por usted, podría repetir todos los números imprimiendo los ifs para que copie + pegue en su código.
Tom Gullen
2
@ Tom, una tabla de búsqueda podría ser mejor, simplemente inicialícela con los valores de 23000 y obtenga el valor en el índice n, donde n es el número que desea el ordinal.
John Boker
2
Col. Metralla. Puedes brillar pero no todos. de todos modos, gracias por mostrar interés en mi pregunta
ArK
@John, idea muy muy inteligente, sería muy rápido acceder y cada índice representa el número que está buscando.
Tom Gullen

Respuestas:

283

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

¿Dónde $numberestá el número que quieres escribir? Funciona con cualquier número natural.

Como una función:

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
fuente
Aunque un poco difícil de entender al principio, ahora creo que representa mejor cómo funciona el sistema de sufijos ordinales para el inglés.
erisco
66
Me gusta tu solución Además, si prefiere no generar 0, modifique la última línea para ser$abbreviation = ($number)? $number. $ends[$number % 10] : $number;
Gavin Jackson
1
@GavinJackson, tu incorporación a esta excelente solución realmente me ayudó (+1). ¿Podría explicarme qué está pasando en el cálculo? Quiero entender. ¡Salud! EDITAR: Se encontró una respuesta: operador condicional
Andrew Fox
Lo sentimos, tuve que romper los 111 votos a 112: D Lo hizo una función en Delphi junto con una aplicación de demostración: pastebin.com/wvmz1CHY
Jerry Dodge
1
@HafezDivandari: ¿estás seguro? Acabo de probar con 7.1.19 y parece funcionar bien
Iacopo
142

PHP tiene una funcionalidad incorporada para esto . ¡Incluso maneja la internacionalización!

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

Tenga en cuenta que esta funcionalidad solo está disponible en PHP 5.3.0 y versiones posteriores.

Jeremy Kauffman
fuente
15
Tenga en cuenta que esto también requiere PECL intl> = 1.0.0
rymo
44
¿Sabes si es posible obtener el número ordinal en forma de palabra? es decir, primero, segundo, tercero, etc. en lugar de primero, segundo, tercero ...
its_me
@jeremy Cuando traté de usar NumberFormatter, siempre arroja un error que NumberFomatter file not found. ¿Cómo resolviste esto?
jhnferraris
1
@Jhn tienes que instalar la extensiónapt-get install php5-intl
Aley
@Aley ya veo. Yii tiene un formateador incorporado que estamos usando en este momento. je
jhnferraris
20

Esto se puede lograr en una sola línea aprovechando una funcionalidad similar en las funciones de fecha / hora integradas de PHP. Humildemente presento:

Solución:

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

Explicación detallada:

La date()función incorporada tiene lógica de sufijo para manejar los cálculos del enésimo día del mes. El sufijo se devuelve cuando Sse da en la cadena de formato:

date( 'S' , ? );

Dado que date()requiere una marca de tiempo (por ?arriba), vamos a pasar nuestro número entero $ncomo el dayparámetro a mktime()y el uso de los valores ficticia 1para el hour, minute, second, y month:

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

Esto realmente falla con gracia en valores fuera de rango para un día del mes (es decir $n > 31), pero podemos agregar una lógica en línea simple para limitar $na 29:

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

El único valor positivo( Mayo de 2017 ) esto falla $n == 0, pero se soluciona fácilmente agregando 10 en ese caso especial:

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

Actualización, mayo de 2017

Como observó @donatJ, lo anterior falla por encima de 100 (por ejemplo, "111"), ya que las >=20comprobaciones siempre devuelven verdadero. Para restablecer estos cada siglo, agregamos un filtro a la comparación:

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

¡Simplemente envuélvelo en una función para mayor comodidad y listo!

Andrew Kozak
fuente
14

Aquí hay una frase:

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

Probablemente la solución más corta. Por supuesto, puede estar envuelto por una función:

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

Saludos, Paul

EDIT1: Corrección de código para 11 a 13.

EDIT2: Corrección de código para 111, 211, ...

EDITAR3: ahora funciona correctamente también para múltiplos de 10.

Pablo
fuente
Me gusta este enfoque, pero por desgracia, no funciona :-( 30 sale como 30. 40 sale como 40. etc.
Flukey
1
Si lo siento Cuando leí la pregunta que pensé, hey, esto debería ser posible con una sola línea de código. Y lo acabo de escribir. Como puede ver en mis ediciones, estoy mejorando. Después de la tercera edición ahora creo que está bastante hecho. Al menos todos los números del 1 al 150 se imprimen bien en mi pantalla.
Paul
Se ve bien hasta 500! (No lo he probado más que eso). ¡Buen trabajo! :-)
Flukey
13

de 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
fuente
9

La respuesta simple y fácil será:

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

//OUTPUT - rd
Caballo blanco
fuente
No dice nada sobre fechas en la pregunta aunque. Elevé mi voto de todos modos: es una solución rápida y simple cuando sabes que el rango de números será <32
cronoklee
8

Escribí esto para PHP4. Ha estado funcionando bien y es bastante económico.

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
fuente
Vishal Kumar, ¿podría ampliar / exponer / explicar eso un poco más? Tks!
iletras
4

solo necesita aplicar la función dada.

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
fuente
3

Genéricamente, puede usar eso y llamar 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
fuente
2

Hice una función que no se basa en la date();función de PHP, ya que no es necesaria, pero también la hice tan compacta y tan corta como creo que es posible actualmente.

El código : (121 bytes en total)

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

Código más compacto a continuación.

Funciona de la siguiente manera :

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.

Cosas que debes saber sobre esta función :

  • Se trata de enteros negativos igual que los enteros positivos y mantiene el signo.
  • Devuelve 11, 12, 13, 811, 812, 813, etc. para los números de dieciséis como se esperaba.
  • No comprueba decimales, pero los dejará en su lugar (uso floor($i), round($i)o ceil($i)al comienzo de la instrucción de retorno final).
  • También puedes agregar format_number($i) al comienzo de la declaración de devolución final para obtener un número entero separado por comas (si está mostrando miles, millones, etc.).
  • Simplemente puede eliminar el $idel principio de la declaración de devolución si solo desea devolver el sufijo ordinal sin lo que ingresó.

Esta función funciona a partir de PHP 5.2, lanzado en noviembre de 2006, simplemente debido a la sintaxis de matriz corta. Si tiene una versión anterior a esta, ¡actualice ya que está casi una década desactualizada! De lo contrario, simplemente reemplace el en línea ['st', 'nd', 'rd']con una variable temporal que contengaarray('st', 'nd', 'rd'); .

La misma función (sin devolver la entrada), pero una vista explosionada de mi función corta para una mejor comprensión:

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
}

Actualización de código :

Aquí hay una versión modificada que es 14 bytes completos más cortos (107 bytes en total):

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

O, para ser lo más corto posible, 25 bytes más cortos (96 bytes en total):

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

Con esta última función, simplemente llame o(121);y hará exactamente lo mismo que las otras funciones que enumeré.

Actualización de Código # 2 :

Ben y yo trabajamos juntos y lo redujimos en 38 bytes (83 bytes en total):

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

¡No creemos que pueda ser más corto que esto! Sin embargo, está dispuesto a demostrar que está equivocado. :)

Espero que todos lo disfruten.

nxasdf
fuente
no necesita usar abs()con módulo%
99 Problemas - La sintaxis no es una
Confiar solo en el módulo seguirá dejando el signo negativo en su lugar. abs();elimina el signo negativo que es lo que necesitaba.
nxasdf
1

Una versión aún más corta para las fechas del mes (hasta 31) en lugar de usar mktime () y no requiere pecl intl:

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

o procesalmente:

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

Esto funciona, por supuesto, porque el mes predeterminado que elegí (enero) tiene 31 días.

Curiosamente, si lo prueba con febrero (u otro mes sin 31 días), se reinicia antes del final:

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

para poder contar hasta los días de este mes con el especificador de fecha ten su ciclo: número de días en el mes.

Dan Dart
fuente
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
fuente
0

Encontré una respuesta en 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
fuente
0

Aquí hay otra versión muy corta que usa las funciones de fecha. Funciona para cualquier número (no limitado por los días del mes) y tiene en cuenta que * 11th * 12th * 13th no sigue el formato * 1st * 2nd * 3rd.

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

Aprecio este pequeño fragmento

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

?>

AQUÍ

niksmac
fuente
1
No estoy muy seguro de lo que ofrece esta respuesta además de la respuesta de ChintanThummar. Bueno, insinúa que ChintanThummar cometió una violación de derechos de autor, a menos que haya escrito el código en su fuente ...
Andreas Rejbrand