regex - Switch-sats för strängmatchning i JavaScript
Hur skriver jag en switch för följande villkor?
Om url:en innehåller "foo", då är settings.base_url "bar".
Följande uppnår den önskade effekten, men jag har en känsla av att detta skulle vara mer hanterbart i en 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/"
};
}
Svar #1
Om du är nöjd med att ditt regex högst upp tar bort allt som du inte vill jämföra i din matchning, behöver du inte en delsträngsjämförelse, och kan göra:
switch (base_url_string) {
case "xxx.local":
// Blah
break;
case "xxx.dev.yyy.com":
// Blah
break;
}
...men återigen, det fungerar bara om det är den fullständiga strängen du matchar. Det skulle misslyckas om base_url_string till exempel var "yyy.xxx.local", medan din nuvarande kod skulle matcha det i "xxx.local"-grenen.
Annars, även om du kan använda en switch för delsträngsmatchning, skulle jag inte rekommendera det i de flesta situationer (mer nedan). Så här skulle det se ut:
function test(str) {
switch (true) {
case /xyz/.test(str):
console.log("• Matched 'xyz' test");
break;
case /test/.test(str):
console.log("• Matched 'test' test");
break;
case /ing/.test(str):
console.log("• Matched 'ing' test");
break;
default:
console.log("• Didn't match any test");
break;
}
}
function test(str) {
console.log("Testing '" + str + "':");
switch (true) {
case /xyz/.test(str):
console.log("• Matched 'xyz' test");
break;
case /test/.test(str):
console.log("• Matched 'test' test");
break;
case /ing/.test(str):
console.log("• Matched 'ing' test");
break;
default:
console.log("• Didn't match any test");
break;
}
}
test("testing");
test("xyz123");
test("foo");
test("fooing");.as-console-wrapper {
max-height: 100% !important;
}Det fungerar på grund av hur JavaScript switch-satser fungerar, särskilt två viktiga aspekter: För det första att fallen betraktas i källtext-ordning, och för det andra att selektorexpressionerna (delarna efter nyckelordet case) är uttryck som utvärderas när det fallet utvärderas (inte konstanter som i vissa andra språk). Så eftersom vårt testuttryck är true, kommer det första case-uttrycket som resulterar i true vara det som används.
Anledningen till att jag inte skulle rekommendera det i de flesta situationer är att det är opraktiskt och dessutom något överraskande (för personer som läser det senare) jämfört med motsvarande if/else if/else:
function test(str) {
if (/xyz/.test(str)) {
console.log("• Matched 'xyz' test");
} else if (/test/.test(str)) {
console.log("• Matched 'test' test");
} else if (/ing/.test(str)) {
console.log("• Matched 'ing' test");
} else {
console.log("• Didn't match any test");
}
}
Live-exempel:
function test(str) {
console.log("Testing '" + str + "':");
if (/xyz/.test(str)) {
console.log("• Matched 'xyz' test");
} else if (/test/.test(str)) {
console.log("• Matched 'test' test");
} else if (/ing/.test(str)) {
console.log("• Matched 'ing' test");
} else {
console.log("• Didn't match any test");
}
}
test("testing");
test("xyz123");
test("foo");
test("fooing");.as-console-wrapper {
max-height: 100% !important;
}I båda fallen gör koden samma saker i samma ordning, men om du inte är väl insatt i JavaScript-arcanum är det senare tydligare (säkert även om du är det).
Svar #2
RegExp kan också användas på inmatningssträngen med match-metoden.
För att säkerställa att vi har en matchning i en case-sats, kommer vi att testa det ursprungliga str-värdet (som ges till switch-satsen) mot input-egenskapen för en lyckad match.
input är en statisk egenskap hos reguljära uttryck som innehåller den ursprungliga inmatningssträngen.
När match misslyckas returnerar det null. För att undvika ett undantagsfel använder vi den valfria kedjeoperatorn (eller den logiska ||-villkorsoperatorn i äldre ES) innan vi får åtkomst till input-egenskapen.
const 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;
}
Ett annat tillvägagångssätt är att använda String()-konstruktorn för att konvertera den resulterande arrayen som måste ha endast 1 element (inga fångstgrupper) och hela strängen måste fångas med kvantifierare (.*) till en sträng. Vid ett misslyckande kommer null-objektet att bli en 'null'-sträng. Det kan verka mindre bekvämt.
const str = 'XYZ test';
switch (str.toLowerCase()) {
case String(str.match(/^xyz.*/i)):
console.log("Matched a string without case sensitivity");
break;
case String(str.match(/.*tes.*/)):
console.log("Matched a string using a substring 'tes'");
break;
}
Hur som helst, en mer elegant lösning är att använda test-metoden istället för match, det vill säga /^find-this-in/.test(str) med switch (true) som helt enkelt returnerar ett booleskt värde och det är enklare att matcha utan skiftlägeskänslighet.
const str = 'haystack';
switch (true) {
case /^hay.*/i.test(str):
console.log("Matched a string that starts with 'hay'");
break;
}
Men att använda if else else if uttalanden i sådana scenarier skulle också vara läsbart
Svar #3
Använd bara egenskapen location.host
switch (location.host) {
case "xxx.local":
settings = ...
break;
case "xxx.dev.yyy.com":
settings = ...
break;
}
Svar #4
Ett annat alternativ är att använda input-fältet i ett regexp-matchresultat:
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;
}
