ตรวจสอบความเท่าเทียมกันของตัวแปรกับรายการค่า


133

ฉันกำลังตรวจสอบตัวแปรพูดfooเพื่อความเท่าเทียมกับค่าต่างๆ ตัวอย่างเช่น,

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

ประเด็นคือรหัสค่อนข้างมากสำหรับงานที่ไม่สำคัญเช่นนี้ ฉันคิดสิ่งต่อไปนี้:

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

แต่สิ่งนี้ก็ไม่ดึงดูดใจฉันอย่างสมบูรณ์เพราะฉันต้องให้ค่าที่ซ้ำซ้อนกับรายการในวัตถุ

มีใครรู้วิธีที่ดีในการตรวจสอบความเท่าเทียมกับค่าหลาย ๆ ค่า?


ฉันคิดว่าบริบทขนาดใหญ่จะต้องมีส่วนร่วมเมื่อตัดสินใจอะไรเช่นนี้เพราะสิ่งสำคัญคือต้องรู้ว่าเหตุใดคุณจึงทำการเปรียบเทียบเช่นนี้
Pointy

ในเกมที่ฉันกำลังสร้างฉันกำลังตรวจสอบรหัสแป้นพิมพ์เพื่อตัดสินใจว่าควรเรียกใช้ฟังก์ชันใด ในเบราว์เซอร์ที่แตกต่างกันคีย์จะมีรหัสคีย์ที่แตกต่างกันดังนั้นจึงจำเป็นต้องเปรียบเทียบกับหลายค่า
pimvdb

ตรวจสอบการทดสอบประสิทธิภาพสำหรับหลายวิธีตัวดำเนินการเชิงตรรกะชนะrunkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

คำตอบ:


175

คุณสามารถใช้อาร์เรย์และindexOf:

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

1
ฉันชอบอันนี้ มันเป็นไปได้ที่จะสร้างฟังก์ชัน 'มี' ผ่านต้นแบบที่ฉันเดาเพื่อกำจัดการใช้> -1
pimvdb

1
@pimvdb: โปรดทราบว่าคุณอาจต้องใช้งานของคุณเองเนื่องจากindexOfมีให้ใช้งานตั้งแต่ ECMAScript 5th edition
Gumbo

ในคำตอบอื่นที่พูดถึงนี้ก็เช่นกันฉันไม่รู้ขอบคุณ
pimvdb

'>' ดีกว่า '! ==' หรือไม่?
Max Waterman

117

ใน ECMA2016 คุณสามารถใช้วิธีการรวม เป็นวิธีที่สะอาดที่สุดที่ฉันเคยเห็นมา (รองรับโดยเบราว์เซอร์หลัก ๆ ทั้งหมดยกเว้น IE (Polyfill อยู่ในลิงค์)

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

4
สำหรับตัวแปรif([a,b,c].includes(foo))หรือสตริงif(['a','b','c'].includes(foo))
Hastig Zusammenstellen

สำหรับตัวแปร => if([a,b,c].includes(foo))เนื่องจากเครื่องหมายคำพูดจะทำให้เป็นสตริง @HastigZusammenstellen
iambinodstha

2
@BinodShrestha ฉันคิดว่านั่นคือสิ่งที่ฉันเขียนเว้นแต่ฉันจะขาดอะไรไป "สำหรับตัวแปร .. หรือสตริง ... "
Hastig Zusammenstellen

16

จากคำตอบที่ให้มาฉันได้สิ่งต่อไปนี้:

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

5
ความดี! ฉันประหลาดใจที่ใช้งานได้ - inเป็นคีย์เวิร์ดใน Javascript ตัวอย่างเช่นการเรียกใช้function in() {}; in()ผลลัพธ์ในข้อผิดพลาดทางไวยากรณ์อย่างน้อยใน Firefox และฉันไม่แน่ใจว่าทำไมโค้ดของคุณถึงไม่ :-)
Ben Blank

3
นอกจากนี้มักถือเป็นการปฏิบัติที่ไม่ดีในการขยายออกไปObject.prototypeเนื่องจากส่งผลกระทบfor (foo in bar)ในทางที่ไม่ดี บางทีเปลี่ยนเป็นfunction contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }และส่งผ่านวัตถุเป็นอาร์กิวเมนต์?
Ben Blank

ขอบคุณสำหรับปฏิกิริยาของคุณและ Google Closure Compiler ยังส่งกลับข้อผิดพลาดในขณะที่รวบรวมในขณะที่โค้ดนี้ทำงานได้อย่างสวยงาม อย่างไรก็ตามมีการแนะนำว่าคลาสผู้ช่วยสามารถทำงานนี้ได้ดีกว่าการขยายObject.prototypeตามที่คุณกล่าวถึง อย่างไรก็ตามฉันชอบวิธีนี้เนื่องจากสัญกรณ์สำหรับการตรวจสอบนั้นน่ารักfoo.in(1, 2, "test", Infinity)ง่ายเหมือนพาย
pimvdb

และการกำหนดฟังก์ชั่นin()วิธีปกติก็ล้มเหลวบน Chrome เช่นกัน แต่ต้นแบบใช้งานได้ ... :)
pimvdb

3
การขยาย Object.prototype เป็นการต่อต้านรูปแบบและไม่ควรใช้ ใช้ฟังก์ชันแทน
Vernon

9

คุณสามารถเขียนif(foo in L(10,20,30))ถ้าคุณกำหนดLให้เป็น

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

    return obj;
};

มันชัดเจนกว่าคำตอบเดิมจริงๆขอบคุณ!
pimvdb

2
ฉันคิดว่าควรสังเกตว่าฟังก์ชันต้นแบบเป็น 'ใน' อาร์เรย์ / ออบเจ็กต์ด้วยเช่นกันดังนั้นหากมีฟังก์ชัน 'ลบ' ในต้นแบบของอาร์เรย์ / อ็อบเจ็กต์มันจะคืนค่าจริงเสมอหากคุณเขียนโค้ด'remove' in L(1, 3, 12)แม้ว่าคุณจะไม่ได้ทำก็ตาม ระบุ 'ลบ' ที่จะใส่ในรายการ
pimvdb

7

สิ่งนี้สามารถขยายและอ่านได้ง่าย:

switch (foo) {
    case 1:
    case 3:
    case 12:
        // ...
        break

    case 4:
    case 5:
    case 6:
        // something else
        break
}

แต่ไม่ง่ายกว่าเสมอไป :)


ฉันใช้วิธีนี้จริง ๆ เพราะฉันต้องการใส่ความคิดเห็นถัดจากแต่ละค่าเพื่ออธิบายว่าค่านั้นเกี่ยวข้องกับอะไรซึ่งเป็นวิธีที่อ่านได้ง่ายที่สุดในการบรรลุเป้าหมาย
pholcroft

@pholcroft หากคุณต้องการอธิบายว่าแต่ละค่าเกี่ยวข้องกับอะไรคุณควรสร้าง enum หรือคลาสและเขียนคุณสมบัติของคุณเพื่อให้มีชื่อที่สื่อความหมาย
red_dorian

6
var a = [1,2,3];

if ( a.indexOf( 1 ) !== -1 ) { }

โปรดทราบว่า indexOf ไม่ได้อยู่ใน ECMAScript หลัก คุณจะต้องมีข้อมูลโค้ดสำหรับ IE และเบราว์เซอร์อื่นที่อาจไม่รองรับ Array.prototype.indexOf

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 โดยตรง ก็ขี้เกียจลงลิงค์. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
meder omuraliev

มีเหตุผลสำหรับการเปลี่ยนบิตหรือไม่เมื่อคุณกำหนด len ถ้าคุณไม่ได้เปลี่ยนบิตจริง ๆ ? มีการบีบบังคับโดยปริยายเกิดขึ้นที่นี่หรือไม่?
jaredad7

3

นอกจากนี้เนื่องจากค่าที่คุณตรวจสอบผลลัพธ์นั้นไม่ซ้ำกันทั้งหมดคุณสามารถใช้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) );


2

ตอนนี้คุณอาจมีทางออกที่ดีกว่าในการแก้ไขสถานการณ์นี้ แต่วิธีอื่นที่ฉันต้องการ

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    
}

ฉันชอบวิธีแก้ปัญหาข้างต้นมากกว่าดัชนีตรวจสอบว่าคุณต้องตรวจสอบดัชนีด้วย

รวมเอกสาร

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

1

ฉันชอบคำตอบที่ได้รับการยอมรับ แต่คิดว่ามันน่าจะดีที่จะทำให้สามารถใช้อาร์เรย์ได้เช่นกันดังนั้นฉันจึงขยายเป็นสิ่งนี้:

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

คุณสามารถลบประเภทการข่มขู่โดยการเปลี่ยนไป=====


1

หากคุณสามารถเข้าถึง Underscore ได้คุณสามารถใช้สิ่งต่อไปนี้:

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

contains เคยทำงานใน Lodash ด้วย (ก่อน V4) ตอนนี้คุณต้องใช้ไฟล์ includes

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

1

นี่คือฟังก์ชั่น arror ตัวช่วยเล็กน้อย:

const letters = ['A', 'B', 'C', 'D'];

function checkInList(arr, val) {
  return arr.some(arrVal => val === arrVal);
}

checkInList(letters, 'E');   // false
checkInList(letters, 'A');   // true

ข้อมูลเพิ่มเติมที่นี่ ...


แนวทางที่ดีมาก: เริ่มจากความคิดของคุณคุณสามารถทำได้โดยไม่ต้องใช้ตัวช่วย:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Giorgio Tempesta

แต่เมื่อฉันเริ่มปรับแต่งโค้ดฉันพบว่ามันincludesกลายเป็นตัวเลือกที่ดีกว่าเช่นเดียวกับคำตอบของ @ alister:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Giorgio Tempesta

0

ฉันใช้ฟังก์ชัน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

-4

สำหรับคนรุ่นหลังคุณอาจต้องการใช้นิพจน์ทั่วไปเป็นทางเลือก รองรับเบราว์เซอร์ที่ค่อนข้างดี (อ้างอิง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>');
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.