Оператор переключения для сопоставления строк в 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будет тем, которое будет использовано.

TJ Crowder
источник
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;
}
Mitar
источник
хороший. В этом случае любое свойство массива также может быть использовано для тестирования, например.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
Geery.S
источник
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;
        }
    });
};

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

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