Instrução Switch para correspondência de string em JavaScript

193

Como escrevo um swtich para a seguinte condicional?

Se o URL contiver "foo", então settings.base_url é "bar".

A seguir, está obtendo o efeito necessário, mas sinto que isso seria mais gerenciável em um switch:

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/"
    };
}
Dr. Frankenstein
fonte

Respostas:

352

Você não pode fazer isso a switchmenos que esteja fazendo uma correspondência completa de cadeias; que está fazendo correspondência de substring . (Isso não é bem verdade, como Sean aponta nos comentários. Veja a nota no final.)

Se você está feliz que a sua expressão regular no topo está removendo tudo o que você não deseja comparar na sua correspondência, não precisa de uma correspondência de substring e pode fazer:

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

... mas, novamente, isso só funciona se for a string completa que você está combinando. Ele falharia se base_url_stringfosse, digamos, "aaa.xxx.local", enquanto o seu código atual corresponderia ao da ramificação "xxx.local".


Atualização : Ok, então tecnicamente você pode usar um switchpara correspondência de substring, mas eu não o recomendaria na maioria das situações. Veja como ( exemplo ao vivo ):

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

Isso funciona devido à maneira como as switchinstruções JavaScript funcionam , em particular dois aspectos principais: primeiro, que os casos são considerados na ordem do texto de origem e, segundo, que as expressões do seletor (os bits após a palavra-chave case) são expressões avaliadas conforme o caso. avaliados (não constantes como em alguns outros idiomas). Portanto, como nossa expressão de teste é true, a primeira caseexpressão que resultar trueserá a que for usada.

TJ Crowder
fonte
91
Eu sei que é velho, mas isso não é bem verdade - você pode realmente fazê-loswitch(true) { case /foo/.test(bar): ....
Sean Kinsey
23
Oh Deus não! A instrução Switch não deve funcionar assim. Isso é simplesmente quebrado, deve ser ilegal fazer coisas assim.
Pijusn 19/07/2013
47
Hoohoo, tão deliciosamente mal.
Aditya MP
41
Todos vocês apenas precisam ampliar sua perspectiva. Esta é a norma em Ruby, exceto que, em vez de ter o feio truelá, você simplesmente deixa de fora todos juntos.
emkman
49
Eu amo isso e não tenho vergonha de admitir.
chrisf 23/07
65

O RegExp pode ser usado na cadeia de entrada não apenas tecnicamente, mas também praticamente com o matchmétodo.

Como a saída de match()é uma matriz, precisamos recuperar o primeiro elemento da matriz do resultado. Quando a correspondência falha, a função retorna null. Para evitar um erro de exceção, adicionaremos o ||operador condicional antes de acessar o primeiro elemento da matriz e testaremos a inputpropriedade que é uma propriedade estática das expressões regulares que contém a sequência de entrada.

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

Outra abordagem é usar o String()construtor para converter a matriz resultante que deve ter apenas 1 elemento (sem grupos de captura) e a cadeia inteira deve ser capturada com quanitifiers ( .*) em uma cadeia. Em caso de falha, o nullobjeto se tornará uma "null"string. Não é conveniente.

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

De qualquer forma, uma solução mais elegante é usar o método /^find-this-in/.test(str)with switch (true), que simplesmente retorna um valor booleano e é mais fácil pesquisar sem distinção entre maiúsculas e minúsculas.

Steven Pribilinskiy
fonte
1
pribilinsiky: você provavelmente deve mencionar que sua terceira solução (usando test ()) exige que você tenha um switch (true).
traday
35

Basta usar a propriedade location.host

switch (location.host) {
    case "xxx.local":
        settings = ...
        break;
    case "xxx.dev.yyy.com":
        settings = ...
        break;
}
Sean Kinsey
fonte
1
Obrigado, uma vez que este é o que eu deveria estar fazendo realmente
Dr. Frankenstein
Você precisa se preocupar com o tipo de variável que você passa para a instrução switch. Ele deve ser uma string. Para ter certeza que você pode fazer switch ("" + location.host).
ceving 11/07/2013
16

Outra opção é usar o inputcampo de um resultado de correspondência regexp :

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
fonte
Agradável. Nesse caso, qualquer propriedade da matriz também pode ser usada para teste, por exemplo.length:
Steven Pribilinskiy
6
var token = 'spo';

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


-> Se a correspondência é feita, a expressão ternária retorna o token original
----> O token original é avaliado por maiúsculas e minúsculas

-> Se a correspondência não for feita, os retornos ternários são indefinidos
----> Case avalia o token em relação ao indefinido, que esperamos que seu token não seja.

O teste ternário pode ser qualquer coisa, por exemplo, no seu caso

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

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

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

é uma expressão ternária.

O teste nesse caso é token.match (/ spo /), que indica a correspondência da string mantida no token com a expressão de expressão regular / spo / (que é a literal literal spo nesse caso).

Se a expressão e a string corresponderem, resultará em true e retornará o token (que é a string na qual a instrução switch está operando).

Obviamente token === token para que a instrução switch seja correspondida e o caso avaliado

É mais fácil entender se você o observar em camadas e entender que o teste de torneamento é avaliado "ANTES" da instrução switch, para que a instrução switch veja apenas os resultados do teste.

James
fonte
Sua resposta é confusa. Você pode revisar e melhorar o exemplo e a explicação?
Falsarella
@falsarella Expliquei a parte que imaginei que você tivesse problemas para entender. Eu não acho que posso dar um exemplo mais simples. Se você tiver mais perguntas ou puder ser mais específico com suas dificuldades, eu posso ajudar mais.
James
Ok, agora eu entendi. Fiquei confuso porque é óbvio que token.match(/spo/)isso corresponderia.
Falsarella
3

Pode ser mais fácil. Tente pensar assim:

  • primeiro pegue uma string entre caracteres regulares
  • depois disso encontre "case"

:

// '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
fonte
Votado. Mas observe:TypeError: url.match(...) is null
1111161171159459134
1

Pode ser tarde demais e tudo, mas eu gostei disso na atribuição de caso :)

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

você pode levar isso adiante e passar uma lista de opções e manipular o regex com |

TacB0sS
fonte
1
Eu também mudaria || {}para || [-1]ou similar para segurança de tipo. Além disso, por que é new RegExpusado, não apenas barras?
Sergey Krasilnikov
realmente não levou tempo para refiná-lo .. no momento em que funcionou, apenas continuei ..... Sinto-me envergonhado agora.
TacB0sS
Não entre em pânico, essa foi apenas a minha escolha;) Na verdade, eu nem tenho certeza se estou certa, tentei aprender algo novo.
Sergey Krasilnikov
Não ... você está correto ... Eu definitivamente poderia ter generify e embelezar .. Vou quando eu chegar a esse código novamente .. será a esperança em breve I :)
TacB0sS