Как сократить блок case case, преобразовав число в название месяца?

110

Есть ли способ написать это на меньшем количестве строк, но при этом легко читаемым?

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;
}
Леон Габан
источник
7
ИМХО ответ видридуча самый подходящий. Вероятно, это не единственная часть вашего кода, которая требует манипуляций с датой (даже несмотря на то, что та, которую вы показали, особенно легко кодируется). Вам следует серьезно подумать об использовании существующих проверенных библиотек Date.
coredump
2
Я не знаю javascript, но разве у него нет хэш-карты, такой как словарь Python или std :: map в C ++?
Masked Man
28
Разве это не должно быть для codereview.stackexchange.com ?
Локо,
2
Так много ответов меняют поведение кода, не принимая во внимание значение по умолчанию, что приводит к неопределенному выводу, который отличается от того, что делает оригинал.
Питер Б.
2
Это не повторяющийся вопрос> :( Это совершенно другой вопрос, однако ответ может быть таким же.
Леон Габан,

Ответы:

199

Определите массив, затем перейдите по индексу.

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

var month = months[mm - 1] || '';
xdazz
источник
23
вместо этого mm - 1вы также можете установить undefinedв качестве первого значения (индекс 0), чтобы индексы массива соответствовали номерам месяцев
Touffy
9
var month = month[(mm -1) % 12]
mpez0
77
@ mpez0 Думаю, я предпочел бы знать, что кому-то удалось придумать месяц номер 15, а не скрывать, вероятно, плохие данные
Изката
21
@Touffy Думаю, я бы остановился mm-1, так что months.length==12.
Teepeemm
48
@Touffy Я бы сказал, что это не вопрос вкуса, а вопрос избегания умного кода . Представьте себе, что вы читаете чью-то чужую [undefined, 'January', 'February', ...]- мне кажется, ваша первая реакция - WTF ?! , что обычно не является хорошим знаком ...
miraculixx
81

как насчет того, чтобы вообще не использовать массив :)

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

согласно этому ответу Получить название месяца от Даты от Дэвида Стори

Видридуч
источник
2
Учитывая постановку рассматриваемой проблемы, ваш ответ на самом деле не решение этой проблемы, а какое-то другое решение, которое может быть правильным в другом контексте. Выбранный ответ по-прежнему остается лучшим и действенным.
TechMaze,
6
new Date("2009-11-10")Гарантированно анализируется только формат (см. Эту спецификацию: ecma-international.org/publications/standards/Ecma-262.htm ). Другие форматы даты (включая один в вашем ответе) могут быть проанализированы, если браузер выберет это, и, следовательно, не являются переносимыми.
jb.
58

Попробуй это:

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

Обратите внимание, что это mmможет быть целое число или строка, и это все равно будет работать.

Если вы хотите, чтобы несуществующие ключи приводили к пустой строке ''(вместо undefined), добавьте эту строку:

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

JSFiddle .

Но я не класс-обертка
источник
4
Для больших наборов данных, чем «месяцы года», это, вероятно, будет более эффективным.
DGM
3
По сути, это перечисление (т.е. сделать его неизменным), определите его как var months = Object.freeze({'1': 'January', '2': 'February'}); //etcСм. Перечисления в JavaScript?
Александр
1
@Alexander Если вы поменяете местами ключ и значения, то да, это похоже на перечисление.
But I'm Not A Wrapper Class
26

Вместо этого вы можете создать массив и найти название месяца:

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


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

См. Ответ @CupawnTae для рационального использования кода || ''

Alex
источник
а не начать с индексом 0 можно сохранить undefinedв 0 качестве var months = [ undefined, 'January','February','March', .....Таким образом , вы будете использоватьmonth = months[mm];
Grijesh CHAUHAN
@GrijeshChauhan: пожалуйста, избегайте «умного» кода. Первая реакция следующего человека должна быть чушь. Это всего лишь '-1', месяцев. Тогда длина будет 13, wtf ^ 2. programmers.stackexchange.com/questions/91854/…
RvdK
19

Быть осторожен!

То, что должно немедленно вызвать тревогу, - это первая строка: var month = '';- почему эта переменная инициализируется пустой строкой, а не nullили undefined? Возможно, это было просто привычкой или копированием / вставкой кода, но если вы не знаете этого наверняка, игнорировать это при рефакторинге кода небезопасно.

Если вы используете массив названий месяцев и измените свой код, var month = months[mm-1];вы измените поведение, потому что теперь для чисел за пределами диапазона или нечисловых значений monthбудет undefined. Вы можете знать, что это нормально, но во многих ситуациях это было бы плохо.

Например, предположим, что вы switchнаходитесь в функции monthToName(mm), и кто-то вызывает вашу функцию следующим образом:

var monthName = monthToName(mm);

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

Теперь, если вы monthName[mm-1]перейдете на использование массива и вернетесь , вызывающий код больше не будет работать должным образом, и он будет отправлять undefinedзначения, когда должен отображать предупреждение. Я не говорю, что это хороший код, но, если вы точно не знаете, как этот код используется, вы не можете делать предположений.

Или, может быть, исходная инициализация была там, потому что некоторый код ниже по строке предполагает, что monthэто всегда будет строка, и делает что-то вроде month.length- это приведет к возникновению исключения для недопустимых месяцев и потенциально полностью уничтожит вызывающий скрипт.

Если вы делаете знать весь контекст - например , все это ваш собственный код, и никто еще никогда не собирается использовать его, и вы доверяете себе , не забывайте , что вы сделали изменения когда - нибудь в будущем - это может быть безопасным , чтобы изменить поведение вроде этого, но очень много ошибок возникает из-за такого предположения, что в реальной жизни вам гораздо лучше программировать в защите и / или тщательно документировать поведение.

Ответ Wasmoo дает все правильно (РЕДАКТИРОВАТЬ: ряд других ответов, включая принятый, теперь тоже исправлен) - вы можете использовать months[mm-1] || ''или, если вы предпочитаете, с первого взгляда сделать более очевидным, что происходит, что-то вроде:

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
источник
1
Никто еще не упомянул об изменении поведения, поэтому это следует учитывать при рефакторинге кода.
Мауро
Это правильный ответ. Большинство других ответов незаметно меняют поведение кода. Это может не иметь значения, или может быть очень сложно найти ошибку.
Питер Б.
Ах, так что лучше всегда инициализировать переменную undefined? Сохраняет ли это производительность при преобразовании типа?
Леон Габан
2
@LeonGaban это не о производительности: исходный вопрос инициализировал переменную пустой строкой и оставил ее так, если не был выбран действительный месяц, тогда как многие другие ответы здесь проигнорировали этот факт и изменили поведение, вернувшись, undefinedкогда ввод не был т 1..12. За исключением очень исключительных обстоятельств, правильное поведение всегда выше производительности.
CupawnTae
17

Для полноты картины хочу дополнить текущие ответы. По сути, вы можете опустить breakключевое слово и напрямую вернуть соответствующее значение. Эта тактика полезна, если значение не может быть сохранено в предварительно вычисленной справочной таблице.

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

Еще раз, использование справочной таблицы или функций даты более кратко и субъективно лучше .

Gerard
источник
16

Вы можете сделать это с помощью массива:

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

var month = months[mm - 1] || '';
Стюарт Вагнер
источник
12

Вот еще один вариант, который использует только 1 переменную и по-прежнему применяет значение по умолчанию, ''когда mmнаходится за пределами диапазона.

var month = ['January', 'February', 'March',
             'April', 'May', 'June', 'July',
             'August', 'September', 'October',
             'November', 'December'
            ][mm-1] || '';
Wasmoo
источник
Также может работать проверка диапазона и выдача исключения. И возврат «Ошибка» или «Не определено» может быть альтернативой пустой строке.
ChuckCottrill
9

Вы можете записать его как выражение вместо переключателя, используя условные операторы:

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

Если вы раньше не видели связанных условных операторов, это может показаться труднее читать. Написание его в виде выражения делает один аспект даже более заметным, чем исходный код; ясно, что целью кода является присвоение значения переменной month.

Guffa
источник
1
Я хотел предложить и это. На самом деле он очень удобочитаем, оставаясь лаконичным, и будет хорошо работать для разреженных сопоставлений и нечисловых ключей, чего нет в решении массива. PS Я тоже получил случайный необъяснимый отрицательный голос за свой ответ - вероятно, тот же художник, проезжающий мимо.
CupawnTae
6

Основываясь на предыдущем ответе Cupawn Tae, я бы сократил его до:

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

В качестве альтернативы, да, я ценю, менее читаемый:

var month = months[mm - 1] || ''; // as mentioned further up
NeilElliott-NSDev
источник
Вы можете пропустить (!!months[mm - 1])и просто сделать months[mm - 1].
YingYang
Это привело бы к undefined, если бы индекс массива был вне допустимого диапазона!
NeilElliott-NSDev
months[mm - 1]вернется undefinedдля индекса, выходящего за пределы допустимого диапазона. Поскольку undefinedэто ложь, вы получите ''значение month.
YingYang
Как указано в других ответах, вы можете еще больше упростить эту строку:var month = months[mm - 1] || '';
YingYang
Хотя я заметил, что дальше (не было, когда я писал), var month = months [mm - 1] || ''; Что было бы еще аккуратнее.
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]
}
Лаксмикант Данге
источник
4

Как и @vidriduch, я хотел бы подчеркнуть важность i20y («интернационализации») кода в современном контексте и предложить следующее краткое и надежное решение вместе с унитарным тестом.

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

Я стараюсь оставаться как можно ближе к исходному вопросу, то есть преобразовывать числа от 1 до 12 в названия месяцев, не только для одного особого случая, но и возвращаться undefinedв случае неверных аргументов, используя некоторую ранее добавленную критику и содержание других ответы. (Изменение с undefinedна ''тривиально, если требуется точное соответствие.)

кортик
источник
0

Я бы выбрал решение wasmoo , но настроил его так:

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

На самом деле это тот же самый код, но с другим отступом, что IMO делает его более читабельным.

Джон Слегерс
источник