Como encurtar o bloco de caixa do switch convertendo um número em um nome de mês?

110

Existe uma maneira de escrever isso em menos linhas, mas ainda facilmente legível?

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
fonte
7
A resposta de IMHO vidriduch é a mais apropriada. Provavelmente, essa não é a única parte do seu código que requer manipulações de Data (embora a que você mostrou seja particularmente fácil de codificar). Você deve considerar seriamente o uso de bibliotecas Date testadas e existentes.
coredump
2
Não sei javascript, mas não tem um hashmap, como o dicionário do Python ou o std :: map do C ++?
Homem Mascarado
28
Não é para codereview.stackexchange.com ?
Loko
2
Tantas respostas mudando o comportamento do código ao não levar em conta o padrão '', o que resulta em uma saída indefinida, que é diferente do que o original faz.
Pieter B
2
Esta não é uma pergunta duplicada> :( esta é uma pergunta completamente diferente, mas a resposta pode ser a mesma.
Leon Gaban

Respostas:

199

Defina uma matriz e obtenha pelo índice.

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

var month = months[mm - 1] || '';
xdazz
fonte
23
em vez de mm - 1você também pode definir undefinedcomo o primeiro valor (índice 0) para que os índices da matriz correspondam aos números dos meses
Touffy
9
var month = month[(mm -1) % 12]
mpez0
77
@ mpez0 Acho que prefiro saber que alguém conseguiu chegar ao mês número 15, em vez de ocultar o que provavelmente são dados
inválidos
21
@Touffy, acho que continuaria com mm-1isso months.length==12.
Teepeemm
48
@Touffy, eu diria que não é uma questão de gosto, mas sim de evitar códigos inteligentes . Imagine-se lendo o de outra pessoa [undefined, 'January', 'February', ...]- acho que sua primeira reação é WTF ?! , o que geralmente não é um bom sinal ...
miraculixx
81

que tal não usar 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" }));

de acordo com esta resposta Obtenha o nome do mês de Data de David Storey

vidriduch
fonte
2
Dada a definição do problema em questão, sua resposta não é realmente resolver esse problema, mas uma solução diferente que pode ser correta em um contexto diferente. A resposta selecionada ainda é a melhor e mais eficiente.
TechMaze
6
Apenas o new Date("2009-11-10")formato tem garantia de análise (consulte esta especificação: ecma-international.org/publications/standards/Ecma-262.htm ). Outros formatos de data (incluindo um em sua resposta) podem ser analisados ​​se o navegador assim escolher e, portanto, não são portáteis.
jb.
58

Experimente isto:

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

Observe que mmpode ser um número inteiro ou uma string e ainda funcionará.

Se você quiser que chaves inexistentes resultem em uma string vazia ''(em vez de undefined), adicione esta linha:

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

JSFiddle .

Mas eu não sou uma classe de wrapper
fonte
4
Em conjuntos de dados maiores do que "meses do ano", isso provavelmente será mais eficiente.
DGM
3
Este é efetivamente um enum (ou seja, torná-lo imutável), defina-o como var months = Object.freeze({'1': 'January', '2': 'February'}); //etcSee Enums in JavaScript?
Alexander
1
@Alexander Se você trocar a chave e os valores, sim, é semelhante a um enum.
Mas eu não sou um Wrapper Class
26

Você pode criar uma matriz e procurar o nome do mês:

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


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

Veja a resposta de @CupawnTae para o raciocínio por trás do código || ''

Alex
fonte
ao invés de começar com 0 índice de você poderia manter undefinedem 0 como var months = [ undefined, 'January','February','March', .....Desta forma, você vai usarmonth = months[mm];
Grijesh Chauhan
@GrijeshChauhan: evite códigos 'inteligentes'. A primeira reação da próxima pessoa seria wtf. É apenas um '-1', meses. O comprimento será então 13, wtf ^ 2. programmers.stackexchange.com/questions/91854/…
RvdK
19

Seja cuidadoso!

O que deve disparar imediatamente o alarme é a primeira linha: var month = '';- por que essa variável está sendo inicializada com uma string vazia, em vez de nullou undefined? Pode ter sido apenas um hábito ou código copiado / colado, mas a menos que você saiba com certeza, não é seguro ignorá-lo quando estiver refatorando o código.

Se você usar uma matriz de nomes de meses e alterar seu código para, var month = months[mm-1];você está mudando o comportamento, porque agora para números fora do intervalo, ou valores não numéricos, monthestarão undefined. Você pode saber que isso é ok, mas existem muitas situações em que isso seria ruim.

Por exemplo, digamos que você switchesteja em uma função monthToName(mm)e alguém a esteja chamando assim:

var monthName = monthToName(mm);

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

Agora, se você mudar para usar uma matriz e retornar monthName[mm-1], o código de chamada não funcionará mais como pretendido e enviará undefinedvalores quando for para exibir um aviso. Não estou dizendo que este é um bom código, mas a menos que você saiba exatamente como o código está sendo usado, você não pode fazer suposições.

Ou talvez a inicialização original estivesse lá porque algum código mais abaixo na linha assume que monthsempre será uma string e faz algo como month.length- isso resultará em uma exceção sendo lançada para meses inválidos e potencialmente matar o script de chamada completamente.

Se você fazer conhecer todo o contexto - por exemplo, é tudo seu próprio código, e mais ninguém nunca vai usá-lo, e você confiar em si mesmo não se esqueça que você fez a algum mudança no futuro - pode ser seguro para alterar o comportamento assim, mas muitos bugs vêm desse tipo de suposição de que na vida real é muito melhor programar defensivamente e / ou documentar o comportamento por completo.

A resposta de Wasmoo acertou (EDITAR: várias outras respostas, incluindo a aceita, também foram corrigidas) - você pode usar months[mm-1] || ''ou, se preferir, deixar mais óbvio o que está acontecendo, 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
fonte
1
Ninguém mais mencionou a mudança de comportamento ainda, então isso deve ser levado em consideração ao refatorar o código.
Mauro
Esta resposta está certa. A maioria das outras respostas altera o comportamento do código de maneira sutil. Isso pode não importar ou pode se tornar irritantemente difícil de encontrar bug.
Pieter B
Ah, então é sempre melhor iniciar uma var para undefined? Isso economiza desempenho se o tipo for convertido?
Leon Gaban
2
@LeonGaban não é sobre desempenho: a pergunta original inicializou a variável com uma string vazia e deixou como se nenhum mês válido fosse selecionado, enquanto muitas das outras respostas aqui ignoraram esse fato e mudaram o comportamento retornando undefinedquando a entrada não foi 't 1..12. Exceto em circunstâncias muito excepcionais, o comportamento correto sempre supera o desempenho.
CupawnTae
17

Para completar, gostaria de complementar as respostas atuais. Basicamente, você pode omitir a breakpalavra - chave e retornar diretamente um valor apropriado. Essa tática é útil se o valor não puder ser armazenado em uma tabela de consulta pré-computada.

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

Mais uma vez, usar uma tabela de consulta ou funções de data é mais sucinto e subjetivamente melhor .

Gerard
fonte
16

Você pode fazer isso usando uma matriz:

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

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

Aqui está outra opção que usa apenas 1 variável e ainda aplica o valor padrão ''quando mmestá fora do intervalo.

var month = ['January', 'February', 'March',
             'April', 'May', 'June', 'July',
             'August', 'September', 'October',
             'November', 'December'
            ][mm-1] || '';
Wasmoo
fonte
A verificação de alcance e o lançamento de uma exceção também podem funcionar. E retornar "Erro" ou "Indefinido" pode ser uma alternativa para a string vazia.
ChuckCottrill
9

Você pode escrevê-lo como uma expressão em vez de um switch, usando operadores condicionais:

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

Se você não viu operadores condicionais encadeados antes, pode parecer mais difícil de ler no início. Escrevê-lo como uma expressão torna um aspecto ainda mais fácil de ver do que o código original; é claro que a intenção do código é atribuir um valor à variável month.

Guffa
fonte
1
Eu pretendia sugerir este também. Na verdade, é muito legível, embora conciso, e funcionaria bem para mapeamentos esparsos e chaves não numéricas, o que a solução de array não funciona. PS Eu também recebi um downvote inexplicável aleatório na minha resposta - provavelmente o mesmo artista drive-by.
CupawnTae
6

Com base na resposta de Cupawn Tae anterior, eu encurtaria para:

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

Como alternativa, sim, agradeço, menos legível:

var month = months[mm - 1] || ''; // as mentioned further up
NeilElliott-NSDev
fonte
Você pode pular (!!months[mm - 1])e simplesmente fazer months[mm - 1].
YingYang
Isso resultaria em indefinido se o índice da matriz estivesse fora do intervalo!
NeilElliott-NSDev
months[mm - 1]retornará undefinedpara um índice que está fora do intervalo. Como undefinedé falso, você acabará com ''o valor de month.
YingYang
Conforme declarado em outras respostas, você pode simplificar esta linha ainda mais:var month = months[mm - 1] || '';
YingYang
Embora eu tenha notado mais acima (não estava por perto quando postei), var mês = meses [mm - 1] || ''; O que seria ainda mais limpo.
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
fonte
4

Como @vidriduch, gostaria de sublinhar a importância de i20y ("internacionalização") do código no contexto atual e sugerir a seguinte solução concisa e robusta em conjunto com o teste unitário.

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

Tento ficar o mais próximo possível da questão original, ou seja, transformar os números de 1 a 12 em nomes de meses, não apenas para um caso especial, mas retornar undefinedem caso de argumentos inválidos, usando algumas das críticas anteriormente adicionadas e conteúdos de outras respostas. (A mudança de undefinedpara ''é trivial, caso a correspondência exata seja necessária.)

Dirk
fonte
0

Eu escolheria a solução do wasmoo , mas ajustaria assim:

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

É exatamente o mesmo código, na verdade, mas recuado de forma diferente, o que o IMO o torna mais legível.

John Slegers
fonte