¿Cómo acortar el bloque de mayúsculas y minúsculas para convertir un número en un nombre de mes?

110

¿Hay alguna forma de escribir esto en menos líneas, pero que aún sea fácil de leer?

var month = '';

switch(mm) {
    case '1':
        month = 'January';
        break;
    case '2':
        month = 'February';
        break;
    case '3':
        month = 'March';
        break;
    case '4':
        month = 'April';
        break;
    case '5':
        month = 'May';
        break;
    case '6':
        month = 'June';
        break;
    case '7':
        month = 'July';
        break;
    case '8':
        month = 'August';
        break;
    case '9':
        month = 'September';
        break;
    case '10':
        month = 'October';
        break;
    case '11':
        month = 'November';
        break;
    case '12':
        month = 'December';
        break;
}
Leon Gaban
fuente
7
En mi humilde opinión, la respuesta de vidriduch es la más apropiada. Probablemente esta no sea la única parte de su código que requiere manipulaciones de fecha (aunque la que mostró es particularmente fácil de codificar). Debería considerar seriamente el uso de bibliotecas de fecha probadas y existentes.
coredump
2
No sé javascript, pero ¿no tiene un mapa de hash, como el diccionario de Python o el std :: map de C ++?
Hombre enmascarado
28
¿No se supone que esto es para codereview.stackexchange.com ?
Loko
2
Tantas respuestas que cambian el comportamiento del código al no tener en cuenta el '' predeterminado, lo que da como resultado una salida indefinida, que es diferente a la que hace el original.
Pieter B
2
Esta no es una pregunta duplicada> :( esta es una pregunta completamente diferente, la respuesta puede ser la misma sin embargo.
Leon Gaban

Respuestas:

199

Defina una matriz, luego obtenga por índice.

var months = ['January', 'February', ...];

var month = months[mm - 1] || '';
xdazz
fuente
23
en lugar de mm - 1, también puede establecerlo undefinedcomo el primer valor (índice 0) para que los índices de la matriz coincidan con los números de los meses
Touffy
9
var month = month[(mm -1) % 12]
mpez0
77
@ mpez0 Creo que preferiría saber que alguien se las arregló para encontrar el mes número 15, en lugar de ocultar lo que probablemente sean datos incorrectos
Izkata
21
@Touffy, creo que me quedaría con mm-1eso months.length==12.
Teepeemm
48
@Touffy Yo diría que eso no es cuestión de gustos, sino de evitar el código inteligente . Imagínese leyendo el de otra persona [undefined, 'January', 'February', ...]. ¿Es mejor que su primera reacción sea WTF? , que generalmente no es una buena señal ...
miraculixx
81

¿qué tal no usar la matriz en absoluto :)

var objDate = new Date("10/11/2009"),
    locale = "en-us",
    month = objDate.toLocaleString(locale, { month: "long" });

console.log(month);

// or if you want the shorter date: (also possible to use "narrow" for "O"
console.log(objDate.toLocaleString(locale, { month: "short" }));

según esta respuesta Obtenga el nombre del mes de la fecha de David Storey

vidriduch
fuente
2
Dado el enunciado del problema en cuestión, su respuesta no es realmente resolver ese problema, sino alguna solución diferente que podría ser correcta en un contexto diferente. La respuesta seleccionada sigue siendo la mejor y la más eficaz.
TechMaze
6
Sólo new Date("2009-11-10")formato está garantizado para ser analizado (véase este specifiation: ecma-international.org/publications/standards/Ecma-262.htm ). Se pueden analizar otros formatos de fecha (incluido uno en su respuesta) si el navegador así lo elige y, por lo tanto, no son portátiles.
jb.
58

Prueba esto:

var months = {'1': 'January', '2': 'February'}; //etc
var month = months[mm];

Tenga en cuenta que mm puede ser un número entero o una cadena y seguirá funcionando.

Si desea que las claves no existentes den como resultado una cadena vacía ''(en lugar de undefined), agregue esta línea:

month = (month == undefined) ? '' : month;

JSFiddle .

Pero no soy una clase de envoltura
fuente
4
En conjuntos de datos más grandes que los "meses del año", esto probablemente será más eficiente.
DGM
3
Esto es efectivamente una enumeración (es decir, hacerla inmutable), definirla como ¿ var months = Object.freeze({'1': 'January', '2': 'February'}); //etcVer enumeraciones en JavaScript?
Alexander
1
@Alexander Si intercambias la clave y los valores, entonces sí, es similar a una enumeración.
Pero no soy una clase de envoltura
26

En su lugar, podría crear una matriz y buscar el nombre del mes:

var months = ['January','February','March','April','May','June','July','August','September','October','November','December']


var month = months[mm-1] || '';

Vea la respuesta de @CupawnTae para conocer la razón detrás del código || ''

Alex
fuente
en lugar de inicio con el índice 0 se podía mantener undefineden 0 tan var months = [ undefined, 'January','February','March', .....De esta manera va a utilizarmonth = months[mm];
Grijesh Chauhan
@GrijeshChauhan: evite el código 'inteligente'. La primera reacción de la siguiente persona sería horrible. Es solo un '-1', meses, la longitud será 13, wtf ^ 2. programmers.stackexchange.com/questions/91854/…
RvdK
19

¡Ten cuidado!

Lo que debería activar inmediatamente las alarmas es la primera línea: var month = '';- ¿por qué esta variable se inicializa en una cadena vacía, en lugar de nulloundefined ? Puede que haya sido un hábito o un código copiado / pegado, pero a menos que lo sepa con certeza, no es seguro ignorarlo cuando esté refactorizando el código.

Si usa una matriz de nombres de meses y cambia su código a var month = months[mm-1];, está cambiando el comportamiento, porque ahora para números fuera del rango, o valores no numéricos, monthseránundefined . Puede que sepa que esto está bien, pero hay muchas situaciones en las que esto sería malo.

Por ejemplo, digamos que switchestá en una función monthToName(mm)y alguien llama a su función de esta manera:

var monthName = monthToName(mm);

if (monthName === '') {
  alert("Please enter a valid month.");
} else {
  submitMonth(monthName);
}

Ahora, si cambia a usar una matriz y regresa monthName[mm-1], el código de llamada ya no funcionará como undefinedse esperaba y enviará valores cuando se supone que debe mostrar una advertencia. No digo que esto sea bueno código, pero a menos que sepa exactamente cómo se usa el código, no puede hacer suposiciones.

O tal vez la inicialización original estaba allí porque algún código más adelante en la línea asume que monthsiempre será una cadena, y hace algo comomonth.length : esto resultará en una excepción para meses inválidos y potencialmente matará el script de llamada por completo.

Si haces saber todo el contexto - por ejemplo, que es todo su propio código, y nadie más es cada vez va a utilizarlo, y confiar en sí mismos no olvidemos que hizo el algún cambio en el futuro - puede ser seguro para cambiar el comportamiento como este, pero muchos errores provienen de este tipo de suposición de que en la vida real es mucho mejor programar a la defensiva y / o documentar el comportamiento a fondo.

La respuesta de Wasmoo lo hace bien (EDITAR: varias otras respuestas, incluida la aceptada, ahora también se han solucionado) : puede usar months[mm-1] || ''o si prefiere que sea más obvio de un vistazo lo que está sucediendo, algo como:

var months = ['January', 'February', ...];

var month;

if (mm >= 1 && m <= 12) {
  month = months[mm - 1];
} else {
  month = ''; // empty string when not a valid month
}
CupawnTae
fuente
1
Nadie más ha mencionado el cambio de comportamiento todavía, por lo que esto debe tenerse en cuenta al volver a factorizar el código.
Mauro
Esta respuesta es acertada. La mayoría de las otras respuestas cambian el comportamiento del código de forma sutil. Esto puede no importar o puede volverse tan irritantemente difícil de encontrar.
Pieter B
¿Entonces siempre es mejor iniciar una var undefined? ¿Eso ahorra rendimiento si el tipo se convierte?
Leon Gaban
2
@LeonGaban no se trata de rendimiento: la pregunta original inicializó la variable en una cadena vacía y la dejó así si no se seleccionó un mes válido, mientras que muchas de las otras respuestas aquí ignoraron ese hecho y cambiaron el comportamiento al regresar undefinedcuando la entrada no era 't 1..12. Excepto en circunstancias muy excepcionales, el comportamiento correcto siempre triunfa sobre el rendimiento.
CupawnTae
17

Para completar, me gustaría complementar las respuestas actuales. Básicamente, puede omitir la breakpalabra clave y devolver directamente un valor apropiado. Esta táctica es útil si el valor no se puede almacenar en una tabla de búsqueda precalculada.

function foo(mm) {
    switch(mm) {
        case '1':  return 'January';
        case '2':  return 'February';
        case '3':  return 'March';
        case '4':  return 'April';
        // [...]
        case '12': return 'December';
    }
    return '';
}

Una vez más, usar una tabla de consulta o funciones de fecha es más conciso y subjetivamente mejor .

Gerard
fuente
16

Podrías hacerlo usando una matriz:

var months = ['January', 'February', 'March', 'April', 
              'May', 'June', 'July', 'August', 
              'September', 'October', 'November', 'December'];

var month = months[mm - 1] || '';
Stuart Wagner
fuente
12

Aquí hay otra opción que usa solo 1 variable y aún aplica el valor predeterminado ''cuando mmestá fuera del rango.

var month = ['January', 'February', 'March',
             'April', 'May', 'June', 'July',
             'August', 'September', 'October',
             'November', 'December'
            ][mm-1] || '';
Wasmoo
fuente
La verificación de rango y el lanzamiento de una excepción también podrían funcionar. Y devolver "Error" o "Indefinido" podría ser una alternativa a la cadena vacía.
ChuckCottrill
9

Puede escribirlo como una expresión en lugar de un interruptor, usando operadores condicionales:

var month =
  mm == 1 ? 'January' :
  mm == 2 ? 'February' :
  mm == 3 ? 'March' :
  mm == 4 ? 'April' :
  mm == 5 ? 'May' :
  mm == 6 ? 'June' :
  mm == 7 ? 'July' :
  mm == 8 ? 'August' :
  mm == 9 ? 'September' :
  mm == 10 ? 'October' :
  mm == 11 ? 'November' :
  mm == 12 ? 'December' :
  '';

Si no ha visto operadores condicionales encadenados antes, esto puede parecer más difícil de leer al principio. Escribirlo como una expresión hace que un aspecto sea aún más fácil de ver que el código original; está claro que la intención del código es asignar un valor a la variable month.

Guffa
fuente
1
Quise sugerir este también. En realidad, es muy legible sin dejar de ser conciso, y funcionaría bien para asignaciones dispersas y claves no numéricas, lo que la solución de matriz no hace. PD: También obtuve un voto negativo inexplicable al azar en mi respuesta, probablemente el mismo artista de drive-by.
CupawnTae
6

Sobre la base de la respuesta anterior de Cupawn Tae, la acortaría a:

var months = ['January', 'February', ...];
var month = (mm >= 1 && mm <= 12) ? months[mm - 1] : '';

Alternativamente, sí, lo agradezco, menos legible:

var month = months[mm - 1] || ''; // as mentioned further up
NeilElliott-NSDev
fuente
Puede omitir (!!months[mm - 1])y simplemente hacer months[mm - 1].
YingYang
¡Eso resultaría en indefinido si el índice de la matriz estuviera fuera de rango!
NeilElliott-NSDev
months[mm - 1]volverá undefinedpara un índice que está fuera de rango. Dado que undefinedes falso, terminará con ''el valor de month.
YingYang
Como se indica en otras respuestas, puede simplificar esta línea aún más:var month = months[mm - 1] || '';
YingYang
Aunque he notado más arriba (no estaba disponible cuando publiqué), var mes = meses [mm - 1] || ''; Lo que sería aún más ordenado.
NeilElliott-NSDev
4
var getMonth=function(month){
   //Return string to number.
    var strMonth = ['January', 'February', 'March',
             'April', 'May', 'June', 'July',
             'August', 'September', 'October',
             'November', 'December'
            ];
    //return number to string.
    var intMonth={'January':1, 'February':2, 'March':3,
             'April':4, 'May':5, 'June':6, 'July':7,
             'August':8, 'September':9, 'October':10,
             'November':11, 'December':12
            };
    //Check type and return 
    return (typeof month === "number")?strMonth[month-1]:intMonth[month]
}
Laxmikant Dange
fuente
4

Como @vidriduch, me gustaría subrayar la importancia de i20y ("internacionalización") del código en el contexto actual y sugerir la siguiente solución concisa y robusta junto con la prueba unitaria.

function num2month(month, locale) {
    if (month != Math.floor(month) || month < 1 || month > 12)
        return undefined;
    var objDate = new Date(Math.floor(month) + "/1/1970");
    return objDate.toLocaleString(locale, {month: "long"});
}

/* Test/demo */
for (mm = 1; mm <= 12; mm++)
    document.writeln(num2month(mm, "en") + " " +
                     num2month(mm, "ar-lb") + "<br/>");
document.writeln(num2month("x", "en") + "<br/>");
document.writeln(num2month(.1, "en") + "<br/>");
document.writeln(num2month(12.5, "en" + "<br/>"));

Intento mantenerme lo más cerca posible de la pregunta original, es decir, transformar los números del 1 al 12 en nombres de meses, no solo para un caso especial, sino también regresar undefineden caso de argumentos no válidos, utilizando algunas de las críticas agregadas anteriormente y el contenido de otros respuestas. (El cambio de undefineda ''es trivial, en caso de que se necesite una coincidencia exacta ).

Puñal
fuente
0

Yo iría por la solución de wasmoo , pero ajústela así:

var month = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
][mm-1] || '';

Es exactamente el mismo código, en realidad, pero con sangría diferente, lo que IMO lo hace más legible.

John Slegers
fuente