【问题标题】:Check variable equality against a list of values根据值列表检查变量是否相等
【发布时间】:2022-01-07 05:44:14
【问题描述】:

我正在检查一个变量,比如foo,是否与多个值相等。例如,

if( foo == 1 || foo == 3 || foo == 12 ) {
    // ...
}

关键在于,对于这样一个微不足道的任务,它的代码相当多。我想出了以下几点:

if( foo in {1: 1, 3: 1, 12: 1} ) {
    // ...
}

但这并不完全吸引我,因为我必须为对象中的项目赋予冗余值。

有没有人知道对多个值进行相等性检查的体面方法?

【问题讨论】:

  • 我认为在决定这样的事情时需要考虑更大的背景,因为了解为什么进行此类比较非常重要。
  • 好吧,在我正在创建的游戏中,我正在检查键盘代码以确定应该调用什么函数。在不同的浏览器中,一个键看起来有不同的键码,因此需要与多个值进行比较。
  • 检查多个方法的性能测试,逻辑运算符获胜runkit.com/pramendra/58cad911146c1c00147f8d8d
  • 这是一个常见的重复目标,用于询问为什么variable === value1 || value2 不起作用。对于那些想知道为什么的人:||&&short-circuit evaluation 一起使用;每个操作数是truthy or falsyx === 1 || 2 表示 (x === 1) || (2) 并且是 true2,因为 || 导致第一个真值或最后一个假值操作数,而 x === "a" || "b"true"b"。由于true2"b" 都是真值,因此将它们置于if 条件中等效于if(true)。只需使用[ "a", "b" ].includes(x)

标签: javascript


【解决方案1】:

你可以使用数组和indexOf:

if ([1,3,12].indexOf(foo) > -1)

【讨论】:

  • 我喜欢这个。我猜甚至可以通过原型创建一个“包含”函数,从而消除 > -1 的使用。
  • @pimvdb:请注意,您可能需要自己的实现,因为 indexOf 仅在 ECMAScript 第 5 版之后才可用。
  • '>' 是否比 '!==' 更好?
  • 你可以用~操作符代替> -1:if ( ~[...].indexOf(foo) ) { ... }
  • @MaxWaterman 首先,绝对没有必要检查结果的类型,因为indexOf 总是返回一个数字,所以!= 就足够了。而且> 更短,所以是的,它更好
【解决方案2】:

在 ECMA2016 中,您可以使用 includes 方法。这是我见过的最干净的方式。 (所有主流浏览器都支持)

if([1,3,12].includes(foo)) {
    // ...
}

【讨论】:

  • 对于变量if([a,b,c].includes(foo)) 或字符串if(['a','b','c'].includes(foo))
  • for variables => if([a,b,c].includes(foo)) 因为引号将使其成为字符串。 @HastigZusammenstellen
  • @BinodShrestha 我认为这就是我写的,除非我遗漏了什么。 “对于变量……或字符串……”
【解决方案3】:

使用提供的答案,我得出以下结论:

Object.prototype.in = function() {
    for(var i=0; i<arguments.length; i++)
       if(arguments[i] == this) return true;
    return false;
}

可以这样称呼:

if(foo.in(1, 3, 12)) {
    // ...
}

编辑:我最近遇到了这个“技巧”,如果值是字符串并且不包含特殊字符,这很有用。对于特殊字符,由于转义而变得丑陋,并且因此更容易出错。

/foo|bar|something/.test(str);

更准确地说,这将检查确切的字符串,但对于简单的相等性测试来说又是更复杂的:

/^(foo|bar|something)$/.test(str);

【讨论】:

  • 天哪!我很惊讶它的工作原理——in 是 Javascript 中的一个关键字。例如,运行function in() {}; in() 会导致语法错误,至少在 Firefox 中是这样,我不确定为什么您的代码不会。 :-)
  • 此外,扩展Object.prototype 通常被认为是不好的做法,因为它会以不幸的方式影响for (foo in bar)。也许将其更改为 function contains(obj) { for (var i = 1; i &lt; arguments.length; i++) if (arguments[i] === obj) return true; return false; } 并将对象作为参数传递?
  • 扩展 Object.prototype 是一种反模式,不应使用。请改用函数。
  • 如上所述扩展Object.prototype 这种方式有严重的缺点,可能会导致许多错误。如果你确实想扩展Object.prototype,请始终通过Object.defineProperty 进行扩展,而无需在描述符中设置enumerable
  • @BenBlank - 仅供参考,使用关键字作为文字属性名称在第 5 版规范(2009 年 12 月)中变得有效,这就是 x.in() 起作用的原因。在第 5 版之前,您必须使用计算属性名称 (x["in"]())。
【解决方案4】:

如果你将L定义为,你可以写if(foo in L(10,20,30))

var L = function()
{
    var obj = {};
    for(var i=0; i<arguments.length; i++)
        obj[arguments[i]] = null;

    return obj;
};

【讨论】:

  • 我认为说原型函数也在数组/对象中是谨慎的 - 所以如果数组/对象的原型中有一个函数'remove',它总是会返回 true,如果你编码'remove' in L(1, 3, 12),虽然你没有指定'remove'被放入列表中。
【解决方案5】:

现在您可能有更好的解决方案来解决这种情况,但我更喜欢其他方式。

const arr = [1,3,12]
if( arr.includes(foo)) { // it will return true if you `foo` is one of array values else false
  // code here    
}

我更喜欢上面的解决方案,而不是 indexOf 检查您还需要检查索引的地方。

includes docs

if ( arr.indexOf( foo ) !== -1 ) { }

【讨论】:

    【解决方案6】:

    这很容易扩展和阅读:

    switch (foo) {
        case 1:
        case 3:
        case 12:
            // ...
            break
    
        case 4:
        case 5:
        case 6:
            // something else
            break
    }
    

    但不一定更容易:)

    【讨论】:

    • 我实际上使用了这种方法,因为我想在每个值旁边放一个注释来描述值相关的内容,这是实现它的最易读的方式。
    • @pholcroft 如果您需要描述每个值与您相关的内容,您应该创建一个枚举或类,并将您的属性编写为具有描述性的名称。
    【解决方案7】:
    var a = [1,2,3];
    
    if ( a.indexOf( 1 ) !== -1 ) { }
    

    请注意,indexOf 不在核心 ECMAScript 中。您需要为 IE 和其他不支持 Array.prototype.indexOf 的浏览器提供 sn-p。

    if (!Array.prototype.indexOf)
    {
      Array.prototype.indexOf = function(searchElement /*, fromIndex */)
      {
        "use strict";
    
        if (this === void 0 || this === null)
          throw new TypeError();
    
        var t = Object(this);
        var len = t.length >>> 0;
        if (len === 0)
          return -1;
    
        var n = 0;
        if (arguments.length > 0)
        {
          n = Number(arguments[1]);
          if (n !== n)
            n = 0;
          else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
            n = (n > 0 || -1) * Math.floor(Math.abs(n));
        }
    
        if (n >= len)
          return -1;
    
        var k = n >= 0
              ? n
              : Math.max(len - Math.abs(n), 0);
    
        for (; k < len; k++)
        {
          if (k in t && t[k] === searchElement)
            return k;
        }
        return -1;
      };
    }
    

    【讨论】:

    • 直接是Mozilla的copy。懒得链接了。 developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
    • 如果您实际上没有移位位,那么在定义 len 时是否存在位移位的原因?这里有什么隐含的强制吗?
    • “请注意,indexOf 不在核心 ECMAScript 中。” 它是在 2009 年 12 月的第 5 版规范中添加的。
    【解决方案8】:

    此外,由于您检查结果所依据的值都是唯一的,您也可以使用Set.prototype.has()

    var valid = [1, 3, 12];
    var goodFoo = 3;
    var badFoo = 55;
    
    // Test
    console.log( new Set(valid).has(goodFoo) );
    console.log( new Set(valid).has(badFoo) );

    【讨论】:

      【解决方案9】:

      我喜欢接受的答案,但认为让它也能接受数组会很整洁,所以我将其扩展为:

      Object.prototype.isin = function() {
          for(var i = arguments.length; i--;) {
              var a = arguments[i];
              if(a.constructor === Array) {
                  for(var j = a.length; j--;)
                      if(a[j] == this) return true;
              }
              else if(a == this) return true;
          }
          return false;
      }
      
      var lucky = 7,
          more = [7, 11, 42];
      lucky.isin(2, 3, 5, 8, more) //true
      

      您可以通过将== 更改为=== 来移除类型强制。

      【讨论】:

        【解决方案10】:

        如果您有权访问下划线,则可以使用以下内容:

        if (_.contains([1, 3, 12], foo)) {
          // ...
        }
        

        contains 以前也可以在 Lodash 中工作(在 V4 之前),现在你必须使用 includes

        if (_.includes([1, 3, 12], foo)) {
          handleYellowFruit();
        }
        

        【讨论】:

          【解决方案11】:

          这是一个小辅助箭头函数:

          const letters = ['A', 'B', 'C', 'D'];
          
          function checkInList(arr, val) {
            return arr.some(arrVal => val === arrVal);
          }
          
          checkInList(letters, 'E');   // false
          checkInList(letters, 'A');   // true
          

          More info here...

          【讨论】:

          • 非常好的方法:从您的想法开始,您也可以在没有辅助函数的情况下完成:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option =&gt; option === valToCheck); // false
          • 但是一旦我开始调整代码,我发现includes 成为更好的选择,正如@alister 的回答:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
          【解决方案12】:

          使用 ES6 语法你可以做到:

          Object.prototype.in = function (...args) {
            return args.includes(this.valueOf());
          }
          

          顺便也检查深度相等。需要 valueOf() 方法,因为 this 是一个对象。

          【讨论】:

            【解决方案13】:

            我只是使用 jQuery inArray 函数和一个值数组来完成此操作:

            myArr = ["A", "B", "C", "C-"];
            
            var test = $.inArray("C", myArr)  
            // returns index of 2 returns -1 if not in array
            
            if(test >= 0) // true
            

            【讨论】:

              【解决方案14】:

              对于您经常比较的较大值列表,首先构造Set 并使用Set#has 进行恒定时间检查可能比Array#includes 以线性时间运行更为理想。

              const values = new Set([1, 2, 12]);
              if(values.has(foo)){
                 // do something
              }
              

              【讨论】:

                【解决方案15】:

                对于后代,您可能希望使用正则表达式作为替代方案。浏览器支持也很好(参考https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#Browser_compatibility

                试试这个

                if (foo.toString().match(/^(1|3|12)$/)) {
                    document.write('Regex me IN<br>');
                } else {
                    document.write('Regex me OUT<br>');
                }
                

                【讨论】:

                  猜你喜欢
                  • 2023-01-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多