Instruction Switch pour la correspondance de chaînes en JavaScript

193

Comment écrire un swtich pour le conditionnel suivant?

Si l'url contient "foo", alors settings.base_url est "bar".

Ce qui suit permet d'obtenir l'effet requis, mais j'ai le sentiment que ce serait plus gérable dans un commutateur:

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
la source

Réponses:

352

Vous ne pouvez pas le faire dans un switchsauf si vous faites une correspondance complète de chaînes; qui fait la correspondance de sous-chaînes . (Ce n'est pas tout à fait vrai, comme Sean le souligne dans les commentaires. Voir la note à la fin.)

Si vous êtes satisfait que votre expression régulière en haut supprime tout ce que vous ne voulez pas comparer dans votre correspondance, vous n'avez pas besoin d'une correspondance de sous-chaîne et pouvez faire:

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

... mais encore une fois, cela ne fonctionne que si c'est la chaîne complète que vous correspondez. Il échouerait si base_url_string, disons, "yyy.xxx.local" alors que votre code actuel correspondrait à celui de la branche "xxx.local".


Mise à jour : OK, donc techniquement, vous pouvez utiliser un switchpour la correspondance de sous-chaînes, mais je ne le recommanderais pas dans la plupart des situations. Voici comment ( exemple en direct ):

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

Cela fonctionne en raison de la façon dont les switchinstructions JavaScript fonctionnent , en particulier de deux aspects clés: premièrement, que les cas sont considérés dans l' ordre du texte source , et deuxièmement que les expressions de sélecteur (les bits après le mot-clé case) sont des expressions qui sont évaluées comme ce cas est évalué (pas de constantes comme dans certains autres langages). Donc, puisque notre expression de test est true, la première caseexpression qui en résulte truesera celle qui sera utilisée.

TJ Crowder
la source
91
Je sais que c'est vieux, mais ce n'est pas tout à fait vrai - vous pouvez le faireswitch(true) { case /foo/.test(bar): ....
Sean Kinsey
23
Oh mon dieu, non! L'instruction Switch n'est pas censée fonctionner comme ça. C'est simplement cassé, il devrait être illégal de faire des choses comme ça.
Pijusn
47
Hoohoo, si délicieusement diabolique.
Aditya MP
41
Vous avez tous juste besoin d'élargir votre perspective. C'est la norme dans Ruby, sauf qu'au lieu d'avoir le laid truelà-bas, vous laissez tout simplement de côté.
emkman
49
J'adore ça et je n'ai pas honte de l'admettre.
chrisf le
65

RegExp peut être utilisé sur la chaîne d'entrée non seulement techniquement mais aussi pratiquement avec la matchméthode.

Comme la sortie de l ' match()est un tableau, nous devons récupérer le premier élément du tableau du résultat. Lorsque la correspondance échoue, la fonction retourne null. Pour éviter une erreur d'exception, nous allons ajouter l' ||opérateur conditionnel avant d'accéder au premier élément du tableau et tester par rapport à la inputpropriété qui est une propriété statique des expressions régulières qui contient la chaîne d'entrée.

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

Une autre approche consiste à utiliser le String()constructeur pour convertir le tableau résultant qui ne doit avoir qu'un seul élément (pas de groupes de capture) et toute la chaîne doit être capturée avec quanitifiers ( .*) en une chaîne. En cas d'échec, l' nullobjet deviendra une "null"chaîne. Pas pratique.

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

Quoi qu'il en soit, une solution plus élégante consiste à utiliser la méthode /^find-this-in/.test(str)with switch (true)qui renvoie simplement une valeur booléenne et il est plus facile de rechercher sans respecter la casse.

Steven Pribilinskiy
la source
1
pribilinsiky: vous devriez probablement mentionner que votre troisième solution (en utilisant test ()) vous oblige à avoir switch (true).
traday
35

Utilisez simplement la propriété location.host

switch (location.host) {
    case "xxx.local":
        settings = ...
        break;
    case "xxx.dev.yyy.com":
        settings = ...
        break;
}
Sean Kinsey
la source
1
Merci, +1 car c'est ce que je devrais faire vraiment
Dr Frankenstein
Vous devez faire attention au type de variable que vous passez à l'instruction switch. Ce doit être une chaîne. Pour être sûr que vous pouvez le faire switch ("" + location.host).
ceving le
16

Une autre option consiste à utiliser le inputchamp d'un résultat de correspondance d'expression régulière :

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
la source
joli. Dans ce cas, toute propriété de tableau peut également être utilisée pour le test, par exemple.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;;
}


-> Si la correspondance est faite, l'expression ternaire renvoie le jeton d'origine
----> Le jeton d'origine est évalué par cas

-> Si la correspondance n'est pas faite, le ternaire renvoie undefined
----> Case évalue le jeton par rapport à undefined, ce qui, espérons-le, n'est pas votre jeton.

Le test ternaire peut être n'importe quoi par exemple dans votre cas

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

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

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

est une expression ternaire.

Le test dans ce cas est token.match (/ spo /) qui indique la correspondance entre la chaîne contenue dans le jeton et l'expression regex / spo / (qui est la chaîne littérale spo dans ce cas).

Si l'expression et la chaîne correspondent, cela donne la valeur true et renvoie le jeton (qui est la chaîne sur laquelle l'instruction switch fonctionne).

Evidemment token === token donc l'instruction switch est mise en correspondance et le cas évalué

Il est plus facile à comprendre si vous le regardez en couches et comprenez que le test de tournage est évalué "AVANT" l'instruction switch de sorte que l'instruction switch ne voit que les résultats du test.

James
la source
Votre réponse est déroutante. Pouvez-vous revoir et améliorer l'exemple et l'explication?
falsarella
@falsarella J'ai expliqué la partie que j'imaginais que vous aviez du mal à comprendre. Je ne pense pas pouvoir faire un exemple plus simple. Si vous avez plus de questions ou pouvez être plus précis avec vos difficultés, je peux vous aider davantage.
James
Ok, maintenant je l'ai. J'étais confus car il est évident que token.match(/spo/)cela correspondrait.
falsarella
3

Cela peut être plus facile. Essayez de penser comme ceci:

  • attraper d'abord une chaîne entre des caractères normaux
  • après cette recherche "cas"

:

// '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
la source
Vote positif. Mais notez:TypeError: url.match(...) is null
1111161171159459134
1

Peut-être trop tard et tout, mais j'ai aimé cela dans l'affectation de cas :)

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

vous pourriez éventuellement aller plus loin, passer une liste d'options et gérer l'expression régulière avec |

TacB0sS
la source
1
Je voudrais aussi changer || {}à || [-1]ou similaire pour la sécurité de type. Aussi, pourquoi est-il new RegExputilisé, pas seulement des barres obliques?
Sergey Krasilnikov
Je n'ai pas vraiment pris le temps de l'affiner ... au moment où cela a fonctionné, j'ai continué ... J'ai honte maintenant.
TacB0sS
Pas de panique, c'était juste ma petite bête;) En fait, je ne suis même pas sûr d'avoir raison, j'ai essayé d'apprendre de nouvelles choses.
Sergey Krasilnikov
Non ... vous avez raison ... J'aurais certainement pu générer et embellir .. Je le ferai quand j'aurai à nouveau ce code .. sera bientôt assez j'espère :)
TacB0sS