【问题标题】:Switch statement for string matching in JavaScriptJavaScript中字符串匹配的switch语句
【发布时间】:2011-02-23 05:00:00
【问题描述】:

如何为以下条件编写开关?

如果网址包含“foo”,那么settings.base_url就是“bar”。

以下实现了所需的效果,但我觉得这在 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/"
    };
}

【问题讨论】:

    标签: javascript regex switch-statement


    【解决方案1】:

    RegExp 也可以通过match 方法用于输入字符串。

    为确保我们在case 子句中有匹配项,我们将针对成功的matchinput 属性测试原始str 值(提供给switch 语句)。

    input is a static property of regular expressions 包含原始输入字符串。

    match 失败时,它返回null。为避免异常错误,我们在访问 input 属性之前使用可选链运算符(或旧版 ES 中的逻辑 || 条件运算符)。

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

    另一种方法是使用 String() 构造函数将结果数组转换为字符串,该数组必须只有 1 个元素(无捕获组)并且必须使用量词 (.*) 捕获整个字符串。如果发生故障,null 对象将变为'null' 字符串。这似乎不太方便。

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

    无论如何,更优雅的解决方案是使用test 方法而不是match,即/^find-this-in/.test(str)switch (true),它只返回一个布尔值,并且不区分大小写更容易匹配。

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

    【讨论】:

    • pribilinsiky:您可能应该提到您的第三个解决方案(使用 test())要求您拥有 switch(true)。
    【解决方案2】:

    你也可以像这样使用默认情况:

        switch (name) {
            case 't':
                return filter.getType();
            case 'c':
                return (filter.getCategory());
            default:
                if (name.startsWith('f-')) {
                    return filter.getFeatures({type: name})
                }
        }
    

    【讨论】:

      【解决方案3】:

      提高工作安全性的独立版本:

      switch((s.match(r)||[null])[0])
      

      function identifyCountry(hostname,only_gov=false){
          const exceptionRe = /^(?:uk|ac|eu)$/ ; //https://en.wikipedia.org/wiki/Country_code_top-level_domain#ASCII_ccTLDs_not_in_ISO_3166-1
          const h = hostname.split('.');
          const len = h.length;
          const tld = h[len-1];
          const sld = len >= 2 ? h[len-2] : null;
      
          if( tld.length == 2 ) {
              if( only_gov && sld != 'gov' ) return null;
              switch(  ( tld.match(exceptionRe) || [null] )[0]  ) {
               case 'uk':
                  //Britain owns+uses this one
                  return 'gb';
               case 'ac':
                  //Ascension Island is part of the British Overseas territory
                  //"Saint Helena, Ascension and Tristan da Cunha"
                  return 'sh';
               case null:
                  //2-letter TLD *not* in the exception list;
                  //it's a valid ccTLD corresponding to its country
                  return tld;
               default:
                  //2-letter TLD *in* the exception list (e.g.: .eu);
                  //it's not a valid ccTLD and we don't know the country
                  return null;
              }
          } else if( tld == 'gov' ) {
              //AMERICAAA
              return 'us';
          } else {
              return null;
          }
      }
      <p>Click the following domains:</p>
      <ul onclick="console.log(`${identifyCountry(event.target.textContent)} <= ${event.target.textContent}`);">
          <li>example.com</li>
          <li>example.co.uk</li>
          <li>example.eu</li>
          <li>example.ca</li>
          <li>example.ac</li>
          <li>example.gov</li>
      </ul>

      不过,老实说,你可以这样做

      function switchableMatch(s,r){
          //returns the FIRST match of r on s; otherwise, null
          const m = s.match(r);
          if(m) return m[0];
          else return null;
      }
      

      然后是switch(switchableMatch(s,r)){…}

      【讨论】:

        【解决方案4】:

        可能为时已晚,但我喜欢这个以防万一分配:)

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

        您可以更进一步,传递选项列表并使用 | 处理正则表达式

        【讨论】:

        • 为了类型安全,我还会将 || {} 更改为 || [-1] 或类似名称。另外,为什么使用new RegExp,而不仅仅是斜线?
        • 并没有真正花时间去完善它。它工作的那一刻我只是继续......我现在感到很惭愧。
        • 别慌,这只是我的吹毛求疵;)事实上我什至不确定我是对的,我试着学习一些新东西。
        • 不......你是对的......我绝对可以进行泛化和美化......
        【解决方案5】:

        这可能更容易。试着这样想:

        • 先捕获常规字符之间的字符串
        • 然后找到“案例”

        // '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
        

        【讨论】:

        • 赞成。但请注意:TypeError: url.match(...) is null
        【解决方案6】:

        另一种选择是使用regexp match resultinput 字段:

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

        【讨论】:

        • 不错的一个。在这种情况下,任何数组属性也可以用于测试,例如.length:
        【解决方案7】:
        var token = 'spo';
        
        switch(token){
            case ( (token.match(/spo/) )? token : undefined ) :
               console.log('MATCHED')    
            break;;
            default:
               console.log('NO MATCH')
            break;;
        }
        


        --> 如果匹配,则三元表达式返回原始标记
        ----> 原始token按大小写评估

        --> 如果不匹配,三元返回 undefined
        ----> Case 根据 undefined 评估令牌,希望您的令牌不是。

        三元测试可以是任何东西,例如你的情况

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

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

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

        ternary expression.

        本例中的测试是 token.match(/spo/),它表明 token 中保存的字符串与正则表达式 /spo/(在本例中为文字字符串 spo)匹配。

        如果表达式和字符串匹配,则结果为真并返回令牌(这是 switch 语句正在操作的字符串)。

        显然是 token === token 所以 switch 语句被匹配并且 case 被评估

        如果您分层查看它并了解turnery测试是在switch语句“之前”评估的,则更容易理解,因此switch语句只看到测试的结果。

        【讨论】:

        • 您的回答令人困惑。你能回顾和改进例子和解释吗?
        • @falsarella 我解释了我认为你无法理解的部分。我不认为我可以举一个更简单的例子。如果您有更多问题或可以更具体地解决您的困难,我可以提供更多帮助。
        • 好的,现在我明白了。我很困惑,因为很明显token.match(/spo/) 会匹配。
        【解决方案8】:

        你不能在 switch 中这样做,除非你正在做 full 字符串匹配;这是在进行 substring 匹配。 (这不是完全正确的,正如 Sean 在 cmets 中指出的那样。请参阅末尾的注释。)

        如果您很高兴顶部的正则表达式删除了您不想在匹配中比较的所有内容,那么您不需要子字符串匹配,并且可以这样做:

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

        ...但同样,这仅在您匹配的 complete 字符串时才有效。如果base_url_string 是“yyy.xxx.local”,而您当前的代码将匹配“xxx.local”分支中的代码,它将失败。


        更新:好的,所以从技术上讲,您可以使用switch 进行子字符串匹配,但在大多数情况下我不推荐它。方法如下 (live example):

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

        这是因为 JavaScript switch statements work 的方式,特别是两个关键方面:首先,以 源文本 顺序考虑案例,其次选择器表达式(后面的位关键字case) 是表达式,在这种情况下被评估(而不是像其他一些语言中的常量)。因此,由于我们的测试表达式是 true,所以第一个导致 truecase 表达式将被使用。

        【讨论】:

          【解决方案9】:

          只需使用 location.host 属性

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

          【讨论】:

          • 谢谢,+1 因为这是我真正应该做的事情
          • 您必须注意传递给 switch 语句的变量类型。它必须是一个字符串。确保你可以做到switch ("" + location.host)
          猜你喜欢
          • 2018-10-02
          • 1970-01-01
          • 1970-01-01
          • 2023-02-20
          • 1970-01-01
          • 2019-11-17
          • 1970-01-01
          • 2020-12-06
          • 2011-01-17
          相关资源
          最近更新 更多