Comment raccourcir le bloc de cas de commutation en convertissant un nombre en nom de mois?

110

Existe-t-il un moyen d'écrire ceci sur moins de lignes, mais toujours facilement lisible?

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;
}
Léon Gaban
la source
7
La réponse de IMHO vidriduch est la plus appropriée. Ce n'est probablement pas la seule partie de votre code qui nécessite des manipulations de date (même si celle que vous avez montrée est particulièrement facile à coder). Vous devriez sérieusement envisager d'utiliser des bibliothèques de dates existantes et testées.
coredump
2
Je ne connais pas javascript, mais n'a-t-il pas de hashmap, comme le dictionnaire Python ou le std :: map de C ++?
Masked Man
28
N'est-ce pas supposé être pour codereview.stackexchange.com ?
Loko
2
Autant de réponses modifiant le comportement du code en ne prenant pas en compte la valeur par défaut '' ce qui se traduit par une sortie indéfinie, différente de celle de l'original.
Pieter B
2
Ce n'est pas une question en double>: (cela pose une question complètement différente, la réponse peut être la même cependant.
Leon Gaban

Réponses:

199

Définissez un tableau, puis obtenez par index.

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

var month = months[mm - 1] || '';
xdazz
la source
23
au lieu de mm - 1vous pouvez également définir undefinedcomme première valeur (index 0) afin que les indices du tableau correspondent aux numéros de mois
Touffy
9
var month = month[(mm -1) % 12]
mpez0
77
@ mpez0 Je pense que je préférerais savoir que quelqu'un a réussi à trouver le mois 15, plutôt que de cacher ce qui est probablement de mauvaises données
Izkata
21
@Touffy je pense que je resterais avec mm-1, donc ça months.length==12.
Teepeemm
48
@Touffy Je dirais que ce n'est pas une question de goût, mais une question d' éviter le code intelligent . Imaginez-vous en train de lire d'autres [undefined, 'January', 'February', ...]- je préfère que votre première réaction soit WTF?! , ce qui n'est généralement pas bon signe ...
miraculixx
81

et ne pas utiliser du tout array :)

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

selon cette réponse Obtenir le nom du mois de Date de David Storey

vidriduch
la source
2
Compte tenu de l'énoncé du problème en question, votre réponse ne résout pas vraiment ce problème, mais une solution différente qui pourrait être correcte dans un contexte différent. La réponse choisie est toujours la meilleure et la plus efficace.
TechMaze
6
Seul le new Date("2009-11-10")format est garanti pour être analysé (voir cette spécification : ecma-international.org/publications/standards/Ecma-262.htm ). D'autres formats de date (dont un dans votre réponse) peuvent être analysés si le navigateur le souhaite, et ne sont donc pas portables.
jb.
58

Essaye ça:

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

Notez que cela mmpeut être un entier ou une chaîne et cela fonctionnera toujours.

Si vous voulez que les clés non existantes entraînent une chaîne vide ''(au lieu de undefined), ajoutez cette ligne:

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

JSFiddle .

Mais je ne suis pas une classe wrapper
la source
4
Sur des ensembles de données plus volumineux que les «mois de l'année», ce sera probablement plus efficace.
DGM
3
Ceci est effectivement une énumération (c'est-à-dire la rendre immuable), définissez-la comme var months = Object.freeze({'1': 'January', '2': 'February'}); //etcSee Enums en JavaScript?
Alexander
1
@Alexander Si vous échangez la clé et les valeurs, alors oui, c'est similaire à une énumération.
Mais je ne suis pas une classe Wrapper
26

Vous pouvez créer un tableau à la place et rechercher le nom du mois:

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


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

Voir la réponse de @CupawnTae pour le rationnel derrière le code || ''

Alex
la source
plutôt que de commencer avec 0 index que vous pourriez garder undefinedà 0 comme var months = [ undefined, 'January','February','March', .....cette façon vous utiliserezmonth = months[mm];
Grijesh Chauhan
@GrijeshChauhan: veuillez éviter le code «intelligent». La première réaction de la personne suivante serait wtf. C'est seulement un '-1', mois. La longueur sera alors 13, wtf ^ 2. programmers.stackexchange.com/questions/91854/…
RvdK
19

Faites attention!

La chose qui devrait immédiatement déclencher des sonneries d'alarme est la première ligne: var month = '';- pourquoi cette variable est-elle initialisée à une chaîne vide, plutôt que nullou undefined? Il peut s'agir simplement d'une habitude ou d'un copier / coller de code, mais à moins que vous ne le sachiez avec certitude, il n'est pas prudent de l'ignorer lorsque vous refactorisez du code.

Si vous utilisez un tableau de noms de mois et que vous modifiez votre code, var month = months[mm-1];vous modifiez le comportement, car maintenant pour les nombres en dehors de la plage, ou les valeurs non numériques, monthseraundefined . Vous savez peut-être que c'est correct, mais il y a de nombreuses situations où ce serait mauvais.

Par exemple, disons que vous switchêtes dans une fonction monthToName(mm)et que quelqu'un appelle votre fonction comme ceci:

var monthName = monthToName(mm);

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

Maintenant, si vous passez à l'utilisation d'un tableau et au retour monthName[mm-1], le code d'appel ne fonctionnera plus comme prévu et il soumettra des undefinedvaleurs lorsqu'il est censé afficher un avertissement. Je ne dis pas que c'est un bon code, mais à moins que vous ne sachiez exactement comment le code est utilisé, vous ne pouvez pas faire d'hypothèses.

Ou peut-être que l'initialisation d'origine était là parce que du code plus loin dans la ligne suppose que ce monthsera toujours une chaîne et fait quelque chose commemonth.length - cela entraînera la levée d'une exception pour des mois invalides et potentiellement tuera complètement le script appelant.

Si vous ne connaissez tout le contexte - par exemple , il est tout votre propre code, et personne d'autre ne va jamais l' utiliser, et vous vous croyez ne pas oublier que vous avez fait le quelque temps de changement dans l'avenir - il peut être sûr de changer le comportement comme ça, mais tellement de bogues proviennent de ce genre d'hypothèse que dans la vraie vie, il vaut mieux programmer de manière défensive et / ou documenter le comportement à fond.

La réponse de Wasmoo est correcte (EDIT: un certain nombre d'autres réponses, y compris celle acceptée, ont maintenant été corrigées) - vous pouvez utiliser months[mm-1] || ''ou si vous préférez rendre plus évident en un coup d'œil ce qui se passe, quelque chose comme:

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
la source
1
Personne d'autre n'a encore mentionné le changement de comportement, donc cela devrait être pris en compte lors de la refactorisation du code.
Mauro
Cette réponse est parfaite. La plupart des autres réponses modifient le comportement du code de manière subtile. Cela peut ne pas avoir d'importance ou cela peut devenir extrêmement difficile à trouver.
Pieter B
Ah donc il est toujours préférable d'initier un var undefined? Cela économise-t-il les performances si le type est converti?
Leon Gaban
2
@LeonGaban ce n'est pas une question de performances: la question d'origine initialisait la variable à une chaîne vide et la laissait à cela si aucun mois valide n'était sélectionné, alors que beaucoup d'autres réponses ici ignoraient ce fait et changeaient le comportement en retournant undefinedlorsque l'entrée n'était pas 't 1..12. Sauf dans des circonstances très exceptionnelles, un comportement correct l'emporte à chaque fois sur les performances.
CupawnTae
17

Par souci d'exhaustivité, j'aimerais compléter les réponses actuelles. Fondamentalement, vous pouvez ignorer le breakmot - clé et renvoyer directement une valeur appropriée. Cette tactique est utile si la valeur ne peut pas être stockée dans une table de recherche précalculée.

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

Encore une fois, utiliser une table de consultation ou des fonctions de date est plus succinct et subjectivement meilleur .

Gérard
la source
16

Vous pouvez le faire en utilisant un tableau:

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

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

Voici une autre option qui n'utilise qu'une seule variable et applique toujours la valeur par défaut ''lorsqu'elle mmest en dehors de la plage.

var month = ['January', 'February', 'March',
             'April', 'May', 'June', 'July',
             'August', 'September', 'October',
             'November', 'December'
            ][mm-1] || '';
Wasmoo
la source
La vérification de la portée et la levée d'une exception peuvent également fonctionner. Et renvoyer "Error" ou "Undefined" peut être une alternative à la chaîne vide.
ChuckCottrill
9

Vous pouvez l'écrire comme une expression au lieu d'un commutateur, en utilisant des opérateurs conditionnels:

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 vous n'avez pas vu d'opérateurs conditionnels chaînés auparavant, cela peut sembler plus difficile à lire au début. L'écrire comme une expression rend un aspect encore plus facile à voir que le code original; il est clair que l'intention du code est d'attribuer une valeur à la variable month.

Guffa
la source
1
Je voulais suggérer celui-ci également. Il est en fait très lisible tout en restant concis, et fonctionnerait bien pour les mappages clairsemés et les clés non numériques, ce que la solution de tableau ne fait pas. PS J'ai aussi reçu un vote négatif inexpliqué sur ma réponse - probablement le même artiste au volant.
CupawnTae
6

En m'appuyant sur la réponse de Cupawn Tae, je la raccourcirais à:

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

Alternativement, oui, j'apprécie, moins lisible:

var month = months[mm - 1] || ''; // as mentioned further up
NeilElliott-NSDev
la source
Vous pouvez sauter (!!months[mm - 1])et faire simplement months[mm - 1].
YingYang
Cela entraînerait une définition indéfinie si l'index du tableau était hors de portée!
NeilElliott-NSDev
months[mm - 1]retournera undefinedpour un index hors de portée. Puisque undefinedc'est faux, vous vous retrouverez avec ''la valeur de month.
YingYang
Comme indiqué dans d'autres réponses, vous pouvez simplifier encore plus cette ligne:var month = months[mm - 1] || '';
YingYang
Bien que j'aie remarqué plus haut (n'était pas là quand j'ai posté), var mois = mois [mm - 1] || ''; Ce qui serait encore plus soigné.
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
la source
4

Comme @vidriduch, je voudrais souligner l'importance d'i20y ("internationalisabilité") du code dans le contexte actuel et suggérer la solution concise et robuste suivante avec le test unitaire.

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

J'essaie de rester aussi proche que possible de la question originale, c'est-à-dire de transformer les nombres 1 à 12 en noms de mois, non seulement pour un cas particulier, mais de revenir undefineden cas d'arguments invalides, en utilisant certaines des critiques précédemment ajoutées et le contenu d'autres réponses. (Le changement de undefinedà ''est trivial, au cas où une correspondance exacte serait nécessaire.)

Poignard
la source
0

Je choisirais la solution wasmoo , mais ajustez-la comme ceci:

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

C'est exactement le même code, vraiment, mais différemment en retrait, que l'OMI le rend plus lisible.

John Slegers
la source