Instrukcja Switch do dopasowywania ciągów w JavaScript


193

Jak napisać swtich dla następujących warunków?

Jeśli adres URL zawiera „foo”, wówczas settings.base_url to „bar”.

Osiągnięcie wymaganego efektu jest następujące, ale wydaje mi się, że łatwiej byłoby to zmienić w przełączniku:

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

Odpowiedzi:


352

Nie możesz tego zrobić, switchchyba że wykonujesz pełne dopasowanie ciągów; to robi dopasowanie podciągów . (To nie do końca prawda, jak zauważa Sean w komentarzach. Patrz uwaga na końcu).

Jeśli jesteś szczęśliwy, że wyrażenie regularne u góry usuwa wszystkie elementy, których nie chcesz porównywać w dopasowaniu, nie potrzebujesz dopasowania do podciągów i możesz:

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

... ale znowu działa to tylko wtedy, gdy jest to pełny ciąg, który pasujesz. Nie powiedzie się, jeśli base_url_stringpowiedzmy „yyy.xxx.local”, podczas gdy twój obecny kod byłby zgodny z tym w gałęzi „xxx.local”.


Aktualizacja : Ok, więc technicznie ty może użyć switchdo podciągu dopasowania, ale nie polecam go w większości sytuacji. Oto jak ( przykład na żywo ):

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

Działa to ze względu na sposób switchdziałania instrukcji JavaScript , w szczególności dwa kluczowe aspekty: Po pierwsze, że przypadki są rozpatrywane w źródłowej kolejności tekstowej , a po drugie, że wyrażenia selektora (bity po słowie kluczowym case) są wyrażeniami, które są oceniane tak, jak to jest oceniane (nie stałe jak w niektórych innych językach). Ponieważ nasze wyrażenie testowe jest true, pierwszym casewyrażeniem, które się truepojawi, będzie to, które zostanie użyte.


91
Wiem, że jest stary, ale to nie do końca prawda - naprawdę możesz to zrobićswitch(true) { case /foo/.test(bar): ....
Sean Kinsey

23
O Boże nie! Instrukcja Switch nie powinna tak działać. Jest to po prostu zepsute, takie rzeczy powinny być nielegalne.
Pijusn,

47
Hoohoo, tak cudownie złe.
Aditya, poseł

41
Musisz tylko poszerzyć swoją perspektywę. Jest to norma w Ruby, z tym wyjątkiem, że zamiast brzydoty true, po prostu pomijasz wszystko razem.
emkman

49
Uwielbiam to i nie wstydzę się do tego przyznać.
chrisf

65

RegExp może być używany w ciągu wejściowym nie tylko technicznie, ale także praktycznie przy użyciu tej matchmetody.

Ponieważ wynik match()jest tablicą, musimy pobrać pierwszy element tablicy wyniku. Gdy dopasowanie się nie powiedzie, funkcja powraca null. Aby uniknąć błędu wyjątku, dodamy ||operator warunkowy przed uzyskaniem dostępu do pierwszego elementu tablicy i przetestujemy pod kątem inputwłaściwości, która jest statyczną właściwością wyrażeń regularnych zawierających ciąg wejściowy.

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

Innym podejściem jest użycie String()konstruktora do przekonwertowania wynikowej tablicy, która musi zawierać tylko 1 element (bez grup przechwytujących), a cały ciąg musi zostać przechwycony za pomocą kwantyfikatorów ( .*) na ciąg. W przypadku awarii nullobiekt stanie się "null"łańcuchem. Niewygodny.

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

W każdym razie bardziej eleganckim rozwiązaniem jest użycie metody /^find-this-in/.test(str)with switch (true), która po prostu zwraca wartość logiczną i łatwiej jest wyszukiwać bez rozróżniania wielkości liter.


1
pribilinsiky: prawdopodobnie powinieneś wspomnieć, że twoje trzecie rozwiązanie (używając test ()) wymaga posiadania przełącznika (prawda).
traday

35

Wystarczy użyć właściwości location.host

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

1
Dzięki, +1, bo to właśnie powinienem robić
Dr. Frankenstein

Musisz zadbać o typ zmiennej przekazywanej do instrukcji switch. To musi być sznurek. Aby mieć pewność, że możesz switch ("" + location.host).
ceving

16

Inną opcją jest użycie inputpola wyniku dopasowania wyrażenia regularnego :

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

niezłe. W takim przypadku do testu można również użyć dowolnej właściwości tablicy, np..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;;
}


-> Jeśli dopasowanie zostanie wykonane, wyrażenie trójskładnikowe zwraca oryginalny token
----> Oryginalny token jest oceniany według wielkości liter

-> Jeśli dopasowanie nie zostanie wykonane, trójka zwraca wartość niezdefiniowaną
----> Case ocenia token względem niezdefiniowanej, która, mam nadzieję, nie jest twoja.

Test trójskładnikowy może być dowolny, na przykład w twoim przypadku

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

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

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

jest wyrażeniem potrójnym.

Testem w tym przypadku jest token.match (/ spo /), który stwierdza dopasowanie łańcucha trzymanego w tokenie do wyrażenia regularnego / spo / (który w tym przypadku jest literalnym ciągiem spo).

Jeśli wyrażenie i ciąg znaków są zgodne, wynikiem jest prawda i zwraca token (ciąg, na którym działa instrukcja switch).

Oczywiście token === token, więc instrukcja switch jest dopasowana i wielkość liter jest oceniana

Łatwiej jest zrozumieć, jeśli spojrzysz na to warstwowo i zrozumiesz, że test tokarski jest oceniany „PRZED” instrukcją przełączania, tak aby instrukcja przełącznika widziała tylko wyniki testu.


Twoja odpowiedź jest myląca. Czy możesz przejrzeć i ulepszyć przykład i wyjaśnienie?
falsarella,

@falsarella Wyjaśniłem część, o której wyobrażałem sobie, że masz problemy ze zrozumieniem. Nie sądzę, żebym mógł zrobić prostszy przykład. Jeśli masz więcej pytań lub możesz dokładniej określić swoje trudności, mogę pomóc.
James

Ok, teraz to rozumiem. Byłem zdezorientowany, ponieważ to oczywiste, że token.match(/spo/)to pasuje.
falsarella,

3

To może być łatwiejsze. Spróbuj myśleć w ten sposób:

  • najpierw złap ciąg między zwykłymi znakami
  • po tym znajdź „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

Pozytywne. Ale uwaga:TypeError: url.match(...) is null
1111161171159459134

1

Może i będzie za późno, ale spodobało mi się to w przypadku zleceń :)

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

możesz wydłużyć to, przekazać listę opcji i obsłużyć wyrażenie regularne za pomocą |


1
Chciałbym również zmienić || {}na || [-1]podobny typ bezpieczeństwa. Ponadto, dlaczego jest new RegExpużywany, a nie tylko ukośniki?
Siergiej Krasiłnikow

tak naprawdę nie poświęciłem czasu na udoskonalenie go ... w chwili, gdy zadziałało, po prostu kontynuowałem ..... Wstydzę się teraz.
TacB0sS,

Nie panikuj, to był po prostu mój wybór:) W rzeczywistości nie jestem nawet pewien, czy mam rację, próbowałem nauczyć się czegoś nowego.
Sergey Krasilnikov,

Nie ... masz rację ... Zdecydowanie mógłbym wygenerować i upiększać .. Będę, gdy ponownie przejdę do tego kodu .. Wkrótce mam nadzieję, że mam nadzieję :)
TacB0sS
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.