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

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.


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.


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

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

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.


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

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 |


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.