Оператор переключения для сопоставления строк в JavaScript


193

Как мне написать swtich для следующих условных выражений?

Если URL содержит «foo», тогда settings.base_url - это «bar».

Следующее - достижение требуемого эффекта, но я чувствую, что это будет более управляемым в коммутаторе:

var doc_location = document.location.href;
var url_strip = new RegExp("http:\/\/.*\/");
var base_url = url_strip.exec(doc_location)
var base_url_string = base_url[0];

//BASE URL CASES

// LOCAL
if (base_url_string.indexOf('xxx.local') > -1) {
    settings = {
        "base_url" : "http://xxx.local/"
    };
}

// DEV
if (base_url_string.indexOf('xxx.dev.yyy.com') > -1) {
    settings = {
        "base_url" : "http://xxx.dev.yyy.com/xxx/"
    };
}

Ответы:


352

Вы не можете сделать это за switchисключением случаев, когда вы делаете полное сопоставление строк; это делает сопоставление подстроки . (Это не совсем так, как указывает Шон в комментариях. См. Примечание в конце.)

Если вы счастливы, что ваше регулярное выражение в верхней части удаляет все, что вы не хотите сравнивать в своем совпадении, вам не нужно совпадение подстроки, и вы можете сделать:

switch (base_url_string) {
    case "xxx.local":
        // Blah
        break;
    case "xxx.dev.yyy.com":
        // Blah
        break;
}

... но опять же, это работает, только если это полная строка, которую вы соответствуете. Он потерпит неудачу, если base_url_string, скажем, будет «yyy.xxx.local», тогда как ваш текущий код будет соответствовать этому в ветке «xxx.local».


Обновление : Хорошо, так что технически вы можете использовать switchдля подстроки соответствия, но я бы не рекомендовал его в большинстве ситуаций. Вот как ( живой пример ):

function test(str) {
    switch (true) {
      case /xyz/.test(str):
        display("• Matched 'xyz' test");
        break;
      case /test/.test(str):
        display("• Matched 'test' test");
        break;
      case /ing/.test(str):
        display("• Matched 'ing' test");
        break;
      default:
        display("• Didn't match any test");
        break;
    }
}

Это работает из-за того, как switchработают операторы JavaScript , в частности два ключевых аспекта: во-первых, случаи рассматриваются в порядке исходного текста , и во-вторых, что выражения селектора (биты после ключевого слова case) являются выражениями , которые оцениваются как этот случай. оценивается (не константы, как в некоторых других языках). Так как наше тестовое выражение имеет значение true, первое caseвыражение, которое приводит к получению, trueбудет тем, которое будет использовано.


91
Я знаю, что он старый, но это не совсем так - вы можете это сделатьswitch(true) { case /foo/.test(bar): ....
— Шон Кинси

23
О боже, нет! Оператор Switch не должен так работать. Это просто сломано, делать подобные вещи незаконно.
— Пиюсн

47
Ооо, так вкусно зло.
— депутат Адитья

41
Тебе просто нужно расширить свою точку зрения. Это норма в Ruby, за исключением того, что вместо уродливого trueвы просто оставляете все это вместе.
— emkman

49
Я люблю это, и мне не стыдно это признать.
— Крис

65

RegExp может использоваться для входной строки не только технически, но и практически вместе с matchметодом.

Поскольку выходные данные match()являются массивом, нам нужно получить первый элемент массива результата. Когда совпадение не удается, функция возвращается null. Чтобы избежать ошибки исключения, мы добавим ||условный оператор перед доступом к первому элементу массива и проверим inputсвойство , являющееся статическим свойством регулярных выражений, которое содержит строку ввода.

str = 'XYZ test';
switch (str) {
  case (str.match(/^xyz/) || {}).input:
    console.log("Matched a string that starts with 'xyz'");
    break;
  case (str.match(/test/) || {}).input:
    console.log("Matched the 'test' substring");        
    break;
  default:
    console.log("Didn't match");
    break;
}

Другой подход заключается в использовании String()конструктора для преобразования результирующего массива, который должен иметь только 1 элемент (без групп захвата), и всю строку необходимо захватывать с помощью квантификаторов ( .*) в строку. В случае сбоя nullобъект станет "null"строкой. Не удобно.

str = 'haystack';
switch (str) {
  case String(str.match(/^hay.*/)):
    console.log("Matched a string that starts with 'hay'");
    break;
}

В любом случае, более элегантным решением является использование метода /^find-this-in/.test(str)with, switch (true)который просто возвращает логическое значение, и его легче искать без учета регистра.


1
прибилинсики: вам, вероятно, следует упомянуть, что для вашего третьего решения (с использованием test ()) требуется, чтобы у вас был параметр switch (true).
— Традиция

35

Просто используйте свойство location.host

switch (location.host) {
    case "xxx.local":
        settings = ...
        break;
    case "xxx.dev.yyy.com":
        settings = ...
        break;
}

1
Спасибо, +1, так как это то, что я должен делать на самом деле
— доктор Франкенштейн

Вы должны позаботиться о типе переменной, передаваемой в оператор switch. Это должна быть строка. Чтобы быть уверенным, что вы можете сделать switch ("" + location.host).
— выступление

16

Другой вариант - использовать inputполе результата соответствия регулярному выражению :

str = 'XYZ test';
switch (str) {
  case (str.match(/^xyz/) || {}).input:
    console.log("Matched a string that starts with 'xyz'");
    break;
  case (str.match(/test/) || {}).input:
    console.log("Matched the 'test' substring");        
    break;
  default:
    console.log("Didn't match");
    break;
}

хороший. В этом случае любое свойство массива также может быть использовано для тестирования, например.length:
— Стивен Прибилинский

6
var token = 'spo';

switch(token){
    case ( (token.match(/spo/) )? token : undefined ) :
       console.log('MATCHED')    
    break;;
    default:
       console.log('NO MATCH')
    break;;
}


-> Если сопоставление установлено, троичное выражение возвращает исходный токен
----> Исходный токен оценивается регистром

-> Если совпадение не выполнено, троичное возвращает неопределенное значение
----> Кейс сравнивает токен с неопределенным, что, как мы надеемся, не является вашим токеном.

Тройной тест может быть чем угодно, например, в вашем случае

( !!~ base_url_string.indexOf('xxx.dev.yyy.com') )? xxx.dev.yyy.com : undefined 

===========================================

(token.match(/spo/) )? token : undefined ) 

это троичное выражение.

Тест в этом случае - token.match (/ spo /), который устанавливает соответствие строки, содержащейся в токене, с выражением регулярного выражения / spo / (в данном случае это буквальная строка spo).

Если выражение и строка соответствуют, это приводит к истине и возвращает токен (то есть строку, над которой работает оператор switch).

Очевидно, токен === токен, так что оператор switch совпадает, а регистр вычисляется

Проще понять, если вы посмотрите на него послойно и поймете, что тест Turnery оценивается «ДО» оператора switch, так что оператор switch видит только результаты теста.


Ваш ответ сбивает с толку. Можете ли вы рассмотреть и улучшить пример и объяснение?
— falsarella

@falsarella Я объяснил ту часть, которую, как мне показалось, тебе было трудно понять. Я не думаю, что могу привести более простой пример. Если у вас есть больше вопросов или вы можете более конкретно рассказать о своих трудностях, я могу помочь больше.
— Джеймс

Хорошо, теперь я получил это. Я был смущен, потому что очевидно, что token.match(/spo/)будет соответствовать.
— falsarella

3

Это может быть проще. Попробуйте подумать так:

  • сначала поймать строку между обычными символами
  • после этого найдите «дело»

:

// 'www.dev.yyy.com'
// 'xxx.foo.pl'

var url = "xxx.foo.pl";

switch (url.match(/\..*.\./)[0]){
   case ".dev.yyy." :
          console.log("xxx.dev.yyy.com");break;

   case ".some.":
          console.log("xxx.foo.pl");break;
} //end switch

Upvoted. Но обратите внимание:TypeError: url.match(...) is null
— 1111161171159459134

1

Может быть уже поздно и все, но мне понравилось это в случае назначения :)

function extractParameters(args) {
    function getCase(arg, key) {
        return arg.match(new RegExp(`${key}=(.*)`)) || {};
    }

    args.forEach((arg) => {
        console.log("arg: " + arg);
        let match;
        switch (arg) {
            case (match = getCase(arg, "--user")).input:
            case (match = getCase(arg, "-u")).input:
                userName = match[1];
                break;

            case (match = getCase(arg, "--password")).input:
            case (match = getCase(arg, "-p")).input:
                password = match[1];
                break;

            case (match = getCase(arg, "--branch")).input:
            case (match = getCase(arg, "-b")).input:
                branch = match[1];
                break;
        }
    });
};

Вы можете продолжить, передать список параметров и обработать регулярное выражение с помощью |


1
Я также изменил || {}бы || [-1]или аналогичный для безопасности типа. Кроме того, почему new RegExpиспользуется, а не просто косая черта?
— Сергей Красильников

на самом деле не потребовалось времени, чтобы уточнить это .. в тот момент, когда это сработало, я просто продолжил ..... мне стыдно
— TacB0sS

Не паникуйте, это была моя придирка;) На самом деле я даже не уверен, что прав, я пытался чему-то научиться.
— Сергей Красильников

Нет ... вы правы ... Я определенно мог бы генерировать и пририсовывать. Я сделаю это, когда снова доберусь до этого кода ... Надеюсь, скоро будет достаточно :)
— TacB0sS
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.