พิจารณาว่าสตริงอยู่ในรายการใน JavaScript หรือไม่


262

ใน SQL เราสามารถดูว่าสตริงอยู่ในรายการดังนี้:

Column IN ('a', 'b', 'c')

เป็นวิธีที่ดีในการทำเช่นนี้ใน JavaScript? การทำสิ่งนี้เป็นสิ่งที่ไม่น่าเชื่อถือ

if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
   // do something
}

และฉันไม่แน่ใจเกี่ยวกับประสิทธิภาพหรือความชัดเจนของสิ่งนี้:

if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
   // do something
}

หรืออาจใช้ฟังก์ชันสวิตช์:

var str = 'a',
   flag = false;

switch (str) {
   case 'a':
   case 'b':
   case 'c':
      flag = true;
   default:
}

if (expression1 || expression2 || flag) {
   // do something
}

แต่นั่นเป็นระเบียบที่น่ากลัว ความคิดใด ๆ

ในกรณีนี้ฉันต้องใช้ Internet Explorer 7 เพราะเป็นหน้าอินทราเน็ตขององค์กร ดังนั้น['a', 'b', 'c'].indexOf(str) !== -1จะไม่ทำงานโดยกำเนิดหากไม่มีไวยากรณ์น้ำตาล


1
คุณช่วยอธิบายความแตกต่างระหว่าง "สตริงที่อยู่ในรายการ" และ "อาร์เรย์รวมถึงวัตถุ" ได้อย่างไร?
MichałPerłakowski

2
@Gothdo เพราะรายการไม่ได้เป็นอาร์เรย์เสมอและสตริงไม่ใช่วัตถุ? มันจะชัดเจนขึ้นได้อย่างไร
ErikE

@ErikE หากเป็นกรณีที่คุณกล่าวถึงในบันทึกย่อดังนั้นคำถามนี้ควรถูกปิดไม่ควรอนุญาตหรือตอบคำถามใด ๆ เพิ่มเติม คำตอบที่โพสต์แล้วมีเพียงพอสำหรับทุกคนที่จะได้รับความช่วยเหลือ
Vikasdeep Singh

@VicJordan บางทีคุณอาจต้องการลบความคิดเห็นของคุณเนื่องจากไม่ได้ใช้อีกต่อไป
ErikE

คำตอบ:


280

คุณสามารถโทรindexOf:

if (['a', 'b', 'c'].indexOf(str) >= 0) {
    //do something
}

15
ปัญหาเดียวของArray.prototype.indexOfมันคือมันจะไม่ทำงานบน IE แต่น่าเสียดายที่ IE8 ขาดวิธีนี้
CMS

2
แต่คุณสามารถกำหนดได้ถ้าคุณต้องการ ดูsoledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer
Jason Hall

นี่คือสินค้า "ของจริง": developer.mozilla.org/en/Core_JavaScript_1.5_Reference/
......

8
@ImJasonH: โค้ดในหน้านั้นแย่จริงๆ IMHO ตัวอย่างเช่นการตรวจสอบว่าArray.indexOfมีอยู่ก่อนที่จะเอาชนะArray.prototype.indexOfซึ่งไม่ใช่สิ่งเดียวกัน ฉันอยากจะแนะนำการดำเนินการโดย Mozilla มีให้ที่นี่: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/
......

1
ฟัง @CMS, @Emtucifor การนำ Mozilla ไปใช้จะดีกว่ามาก
Jason Hall

230

EcmaScript 6

หากคุณใช้ ES6 คุณสามารถสร้างอาร์เรย์ของรายการและใช้includes:

['a', 'b', 'c'].includes('b')

ซึ่งมีประโยชน์โดยธรรมชาติบางกว่าindexOfเพราะมันถูกต้องสามารถทดสอบสำหรับการปรากฏตัวของNaNในรายการและสามารถตรงกับองค์ประกอบมากมายที่ขาดหายไปเช่นหนึ่งกลางในการ[1, , 2] ยังทำงานบนพิมพ์อาร์เรย์ JavaScriptเช่นundefinedincludesUint8Array

หากคุณกังวลเกี่ยวกับการสนับสนุนเบราว์เซอร์ (เช่น IE หรือ Edge) คุณสามารถตรวจสอบได้Array.includesที่ CanIUse.Comและหากคุณต้องการกำหนดเป้าหมายเบราว์เซอร์หรือเบราว์เซอร์เวอร์ชันที่ขาดหายไปincludesฉันแนะนำpolyfill.ioสำหรับ polyfilling

โดยไม่ต้องมีอาร์เรย์

คุณสามารถเพิ่มisInListคุณสมบัติใหม่ให้กับสตริงได้ดังนี้:

if (!String.prototype.isInList) {
   String.prototype.isInList = function() {
      let value = this.valueOf();
      for (let i = 0, l = arguments.length; i < l; i += 1) {
         if (arguments[i] === value) return true;
      }
      return false;
   }
}

จากนั้นใช้งานเช่น:

'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false

คุณสามารถทำสิ่งเดียวกันNumber.prototypeได้

Array.indexOf

หากคุณใช้เบราว์เซอร์ที่ทันสมัยใช้indexOfงานได้เสมอ อย่างไรก็ตามสำหรับ IE8 และก่อนหน้านี้คุณจะต้องมี polyfill

หากindexOfส่งคืน -1 รายการจะไม่อยู่ในรายการ จะมี แต่สติว่าวิธีการนี้จะไม่ถูกต้องตรวจสอบNaNและในขณะที่มันสามารถจับคู่ชัดเจนundefinedก็ไม่สามารถจับคู่เป็นองค์ประกอบที่ขาดหายไปในขณะที่ในอาร์เรย์undefined[1, , 2]

Polyfill สำหรับindexOfหรือincludesใน IE หรือเบราว์เซอร์ / รุ่นอื่น ๆ ที่ขาดการสนับสนุน

หากคุณไม่ต้องการใช้บริการเช่นpolyfill.ioดังที่ได้กล่าวไว้ข้างต้นคุณสามารถรวมไว้ใน polyfills ที่กำหนดเองตามมาตรฐานซอร์สโค้ดของคุณเอง ยกตัวอย่างเช่น Mozilla indexOfพัฒนาเครือข่ายมีหนึ่งสำหรับ

ในสถานการณ์นี้ที่ฉันต้องแก้ปัญหาสำหรับ Internet Explorer 7 ฉัน "ย้อนกลับรุ่นของตัวเอง" indexOf()ฟังก์ชั่นที่ง่ายขึ้นซึ่งไม่ได้มาตรฐาน:

if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(item) {
      var i = this.length;
      while (i--) {
         if (this[i] === item) return i;
      }
      return -1;
   }
}

อย่างไรก็ตามฉันไม่คิดว่าการแก้ไขArray.prototypeเป็นคำตอบที่ดีที่สุดในระยะยาว การแก้ไขObjectและArrayต้นแบบใน JavaScript สามารถนำไปสู่ข้อผิดพลาดร้ายแรง คุณต้องตัดสินใจว่าการทำเช่นนั้นปลอดภัยในสภาพแวดล้อมของคุณหรือไม่ สิ่งสำคัญอันดับแรกคือการวนซ้ำอาร์เรย์ (เมื่อ Array.prototype ได้เพิ่มคุณสมบัติ) ด้วยfor ... inจะส่งคืนชื่อฟังก์ชันใหม่เป็นหนึ่งในคีย์:

Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!

รหัสของคุณอาจใช้งานได้ในขณะนี้ แต่ขณะที่บางคนในอนาคตเพิ่มห้องสมุด JavaScript หรือปลั๊กอินของบุคคลที่สามซึ่งไม่ได้ปกป้องคีย์ที่สืบทอดมาอย่างกระตือรือร้นทุกอย่างสามารถทำลายได้

วิธีการแบบเก่าเพื่อหลีกเลี่ยงความแตกแยกที่อยู่ในระหว่างการนับเพื่อตรวจสอบแต่ละค่าเพื่อดูว่าวัตถุที่จริงมีว่ามันเป็นสถานที่ให้บริการที่ไม่ได้รับการถ่ายทอดด้วยif (arr.hasOwnProperty(x))และเพียงแล้วxการทำงานกับ

วิธี ES6 ใหม่เพื่อหลีกเลี่ยงปัญหากุญแจพิเศษนี้คือการใช้ofแทนin, for (let x of arr). อย่างไรก็ตามหากคุณไม่สามารถรับประกันได้ว่ารหัสและห้องสมุดของบุคคลที่สามทั้งหมดของคุณยึดตามวิธีนี้อย่างเคร่งครัดดังนั้นสำหรับวัตถุประสงค์ของคำถามนี้คุณอาจต้องการใช้includesตามที่ระบุไว้ข้างต้น


2
นี่อีกดีPolyfillสำหรับindexOfให้บริการโดย MDN โดยทั่วไปแล้วมันทำสิ่งเดียวกัน แต่มีวงจรไฟฟ้าสั้น ๆ สองตัวเพื่อการประเมินที่ง่าย
KyleMit

1
Downvoter: โปรดแสดงความคิดเห็น มีปัญหาอะไรกับเรื่องนี้? ฟังก์ชั่นนี้ไม่ได้มาตรฐานและไม่พยายาม
ErikE

คุณควรใช้สิ่งที่ทดสอบแล้วเช่น polyfill ที่บันทึกโดยลิงก์ @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

@lmiguelmh แล้ว "polyfill" ที่ฉันโพสต์ลิงก์ด้วยตนเองจาก Mozilla docs ตัวเองล่ะ? ไม่ว่าในกรณีใดฟังก์ชั่นนี้ง่ายมากจนฉันไม่ได้กังวลเกี่ยวกับการทดสอบมากนัก และใครก็ตามที่ไม่ควรใช้ฟังก์ชั่น "ทดสอบแล้ว" แต่ควรทดสอบสิ่งที่พวกเขาใช้เอง ดังนั้นความคิดเห็นของคุณจึงถูกวางผิดที่เล็กน้อย คุณระบุข้อบกพร่องเฉพาะกับหน้าที่ของฉันหรือไม่?
ErikE

@ErikE คุณถูกต้องฟังก์ชั่นนี้จะตายและใครก็ตามที่ใช้คำตอบของคุณควรรู้ ฉันไม่เห็นลิงก์ของคุณในตอนแรกเพราะฉันกำลังมองหา "คำตอบ"
lmiguelmh

53

คำตอบส่วนใหญ่แนะนำArray.prototype.indexOfวิธีการปัญหาเดียวคือมันจะไม่ทำงานบนIE เวอร์ชันใด ๆก่อน IE9

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

if (/Foo|Bar|Baz/.test(str)) {
  // ...
}


if (str.match("Foo|Bar|Baz")) {
  // ...
}

อืมขอบคุณที่พูดถึงมัน CMS มันเกิดขึ้นเพียงว่านี่เป็นอินทราเน็ตขององค์กรและพวกเขาใช้ ... เดาว่า ... IE เนื่องจากวิธีการแสดงออกปกติทำให้ฉันมีความประสงค์ฉันจะต้องทำฟังก์ชั่นที่วนซ้ำหรือใช้วิธีวัตถุที่ฉันแนะนำในโพสต์ของฉัน (บล็อกรหัสที่สาม)
ErikE

13
สิ่งนี้จะตรงกับ"HiFooThere"- ฉันไปด้วย/^(?:Foo|Bar|Baz)$/แทน (จุดเริ่มต้นของสตริง, กลุ่มที่ไม่จับภาพ, จุดสิ้นสุดของสตริง)
TrueWill

indexOfได้รับการสนับสนุนในขณะนี้ใน IE 9 ขึ้นไปตามMDN
krock

28

อาร์เรย์มีindexOfวิธีการที่สามารถใช้ในการค้นหาสตริง:

js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1

3
สิ่งนี้จะล้มเหลวในเบราว์เซอร์รุ่นเก่า
epascarello

โอ๊ะโอ ... พูดถึงว่านี่ใช้งานไม่ได้ใน IE น่าจะดี :)
ErikE

2
@epascarello: ไม่เพียง แต่ในเบราว์เซอร์รุ่นเก่าเท่านั้นมันจะล้มเหลวบนIE ใด ๆแม้แต่ใน IE8 :(
CMS

12

เคล็ดลับที่ฉันใช้คือ

>>> ("something" in {"a string":"", "somthing":"", "another string":""})
false
>>> ("something" in {"a string":"", "something":"", "another string":""})
true

คุณสามารถทำสิ่งที่ชอบ

>>> a = ["a string", "something", "another string"];
>>> b = {};
>>> for(var i=0; i<a.length;i++){b[a[i]]="";} /* Transform the array in a dict */
>>> ("something" in b)
true

รอบโลกกำลังใช้ "ใน" ใด ๆ ที่เร็วกว่า / ช้ากว่า / ดีกว่า / แย่กว่าการพยายามที่จะตรวจสอบรายการที่บล็อกรหัสที่สามของฉันในคำถามของฉันแสดง? นอกจากนี้หากคุณกำลังวนรอบสิ่งฉันคิดว่าคุณอาจตรวจสอบองค์ประกอบในอาร์เรย์ในเวลานั้น ... เพียงแค่วนซ้ำในฟังก์ชัน และสำหรับสิ่งที่คุ้มค่าvar i=a.length;while (i--) {/*use a[i]*/}คือวิธีวนรอบที่เร็วที่สุด (ถ้ายอมรับลำดับย้อนกลับ)
ErikE

@Emtucifor: มันขึ้นอยู่กับสิ่งที่คุณกำลังทำและฉันเดาว่ามันอาจจะทำงานแตกต่างกันในเครื่องมือจาวาสคริปต์ที่แตกต่างกัน หากข้อมูลของคุณต้องการการใช้พจนานุกรม ณ จุดใด ๆ ก็จะเป็นการดีกว่าถ้าจะสร้างด้วยวิธีนี้ ฉันคิดว่าสิ่งนี้จะเร็วขึ้นเนื่องจากรายละเอียดการนำไปใช้งานของเอ็นจิ้น (การใช้ตารางแฮชสำหรับวัตถุ) เมื่อสร้างวัตถุ dictแล้ว
Esteban Küber

ใน JavaScript ก็ไม่ได้เรียกว่ากิงดิคก็เรียกว่าวัตถุ
โซโลมอน Ucko

8

นี่คือของฉัน:

String.prototype.inList=function(list){
    return (Array.apply(null, arguments).indexOf(this.toString()) != -1)
}

var x = 'abc';
if (x.inList('aaa','bbb','abc'))
    console.log('yes');
else
    console.log('no');

อันนี้เร็วกว่าถ้าคุณโอเคกับการส่งอาเรย์:

String.prototype.inList=function(list){
    return (list.indexOf(this.toString()) != -1)
}

var x = 'abc';
if (x.inList(['aaa','bbb','abc']))
    console.log('yes')

นี่คือ jsperf: http://jsperf.com/bmcgin-inlsit


ตรงไปตรงมาอย่างใดอย่างหนึ่งที่คุณผ่านอาร์เรย์อาจจะมีประโยชน์มากขึ้นและมันอาจควรจะอยู่ในArray's ต้นแบบ: Array.prototype.containsบางทีสิ่งที่ต้องการ
โซโลมอน Ucko

6

RegExpเป็นสากล แต่ฉันเข้าใจว่าคุณกำลังทำงานกับอาร์เรย์ ดังนั้นตรวจสอบวิธีการนี้ ฉันเคยใช้มันและมันก็มีประสิทธิภาพและรวดเร็วมาก!

var str = 'some string with a';
var list = ['a', 'b', 'c'];
var rx = new RegExp(list.join('|'));

rx.test(str);

นอกจากนี้คุณยังสามารถใช้การปรับเปลี่ยนบางอย่างเช่น:

หนึ่งในสายการบิน

new RegExp(list.join('|')).test(str);

กรณีตาย

var rx = new RegExp(list.join('|').concat('/i'));


และอื่น ๆ อีกมากมาย!


การใช้ regex นั้นจะต้องหลีกเลี่ยงอักขระพิเศษ นอกจากนี้ยังมีความชัดเจนน้อยกว่า ฉันไม่คิดว่ามันจะเป็นทางออกที่ดี
ErikE

@ErikE ฉันเข้าใจการจองของคุณดังนั้นฉันจึงได้อัปเดตคำตอบของฉันโดยเพิ่มรหัสอื่นซึ่งไม่ได้ใช้ RegExp มันเข้ากันได้กับ IE ย้อนหลังได้มากมีสำนวนและรวดเร็ว
sospedra

รหัสเพิ่มเติมของคุณใกล้เคียงกับคำตอบของฉันซึ่งฉันให้ไว้ 5 ปีก่อนที่คุณจะตัดสินใจโพสต์โซลูชัน Regex ขอบคุณสำหรับการเข้าร่วมและในเวลาเดียวกันฉันคิดว่าการย้อนกลับไปยังคำตอบก่อนหน้าของคุณนั้นดีที่สุด
ErikE

@ErikE ใช่คุณพูดถูกฉันไม่ได้ใช้เพื่อตรวจสอบคำตอบในคำถาม และฉันก็เห็นด้วยว่ามันคล้ายกันมาก
sospedra

6

ใช้ indexOf (ไม่ทำงานกับ IE8)

if (['apple', 'cherry', 'orange', 'banana'].indexOf(value) >= 0) {
    // found
}

เพื่อรองรับ IE8 คุณสามารถใช้ indexOf ของ Mozilla

if (!Array.prototype.indexOf) {
    // indexOf polyfill code here
}

นิพจน์ปกติผ่าน String.prototype.match (เอกสาร)

if (fruit.match(/^(banana|lemon|mango|pineapple)$/)) {

}

1
คุณสังเกตเห็นว่าคุณกำลังทำซ้ำคำตอบอื่น ๆ ในหน้า? สิ่งนี้ไม่ได้เพิ่มคุณค่าใด ๆ
ErikE

5

ดูเหมือนว่าคุณจำเป็นต้องใช้ฟังก์ชั่น in_array

jQuery -> inArray

ต้นแบบ -> Array.indexOf

หรือดูตัวอย่างเหล่านี้หากคุณไม่ได้ใช้ jQuery หรือ Prototype:

โวหารหมายเหตุ:ตัวแปรที่มีชื่อว่าสิ่งนั้นควรได้รับการตั้งชื่อเพื่อบอกคุณบางอย่างเกี่ยวกับสิ่งที่พวกเขามี (คำนาม)


โอ้พวกเขาไม่ใช่ตัวแปร แต่มีความหมายว่าเป็นตัวยึดตำแหน่งแบบสุ่มสำหรับการแสดงออก ... เป็นเพียงตัวอย่างของวิธีที่ฉันวางแผนที่จะใช้สคริปต์
ErikE

5

นอกจากนี้indexOf(ซึ่งมีผู้โพสต์อื่นแนะนำ) การใช้Enumerable.include ()ของต้นแบบสามารถทำให้สิ่งนี้เป็นระเบียบและรัดกุมยิ่งขึ้น:

var list = ['a', 'b', 'c'];
if (list.include(str)) {
  // do stuff
}

5
Enumerable.include () เลิกใช้แล้ว แต่Array.prototype.includes ()กำลังจะมาถึงและทำงานในเบราว์เซอร์ส่วนใหญ่แล้วยกเว้น IE (แน่นอน) และ Edge (แน่นอน)
หนวดขาว

2

ขอบคุณสำหรับคำถามและวิธีแก้ปัญหาโดยใช้วิธี Array.indexOf

ฉันใช้รหัสจากโซลูชันนี้เพื่อสร้างฟังก์ชั่น inList () ที่จะ IMO ทำให้การเขียนง่ายขึ้นและการอ่านชัดเจนขึ้น:

function inList(psString, psList) 
{
    var laList = psList.split(',');

    var i = laList.length;
    while (i--) {
        if (laList[i] === psString) return true;
    }
    return false;
}

การใช้:

if (inList('Houston', 'LA,New York,Houston') {
  // THEN do something when your string is in the list
}

1
'Houston'.inList(['LA', 'New York', 'Houston'])จาวาสคริอักษรอาร์เรย์จึงง่ายผมไม่เห็นเหตุผลที่คุณจะแยกออกเมื่อคุณสามารถทำ บางทีif (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}หรือใช้whileวิธีการของคุณ
ErikE

0

ฉันประหลาดใจที่ไม่มีใครพูดถึงฟังก์ชั่นง่าย ๆ ที่รับสายและรายการ

function in_list(needle, hay)
{
    var i, len;

    for (i = 0, len = hay.length; i < len; i++)
    {
        if (hay[i] == needle) { return true; }
    }

    return false;
}

var alist = ["test"];

console.log(in_list("test", alist));

จิมทำตามที่คุณแนะนำแซมในวันที่ 27 ส.ค. 54 เวลา 1:29 น. ในความเป็นจริงคำตอบที่ฉันเลือกนั้นค่อนข้างเหมือนกันเพียงแค่ส่งสตริงกับสิ่งนี้แทนที่จะเป็นพารามิเตอร์
ErikE

@ErikE ขออภัยคำตอบ Jims ดูแปลกสำหรับฉันอย่างที่คุณพูด และคำตอบที่คุณยอมรับจะส่งคืนintซึ่งบางคนอาจเจอคำถามนี้ที่ต้องการรับboolผลตอบแทน คิดว่าอาจช่วยคนไม่กี่คน
ซามูเอลพาร์กินสัน

0

โซลูชันของฉันให้ผลลัพธ์เป็นไวยากรณ์ดังนี้:

// Checking to see if var 'column' is in array ['a', 'b', 'c']

if (column.isAmong(['a', 'b', 'c']) {
  // Do something
}

และฉันใช้สิ่งนี้โดยขยายต้นแบบต้นแบบพื้นฐานเช่นนี้

Object.prototype.isAmong = function (MyArray){
   for (var a=0; a<MyArray.length; a++) {
      if (this === MyArray[a]) { 
          return true;
      }
   }
   return false;
}

เราอาจตั้งชื่อเมธอด isInArray (แต่อาจไม่ใช่ inArray) หรือเพียงแค่ isIn

ข้อดี: ง่ายตรงไปตรงมาและจัดทำเอกสารด้วยตนเอง


อาจมีปัญหาในการขยายวัตถุ bolinfest.com/javascript/inheritance.phpภายใต้ "ทีมงาน Google แผนที่ได้เรียนรู้วิธีนี้อย่างหนัก" และเข้ากันไม่ได้กับการใช้งานเบราว์เซอร์หรือรหัสผู้ใช้อื่น ๆ ฉันยังคิดว่าคำตอบของ ErikE นั้นดีที่สุดเนื่องจากการวนซ้ำแถวนั้นช้ากว่าการค้นหาคีย์ใน hashmap เมื่อ hashmap ถูกสร้างขึ้น: myValues[key];ที่ myValues ​​เป็นวัตถุและคีย์คือสตริงหรือตัวเลขใด ๆ
HMR

-1

คำตอบของSLaksรุ่นที่เรียบง่ายก็ใช้งานได้:

if ('abcdefghij'.indexOf(str) >= 0) {
    // Do something
}

.... เนื่องจากสตริงเป็นอาร์เรย์ของตัวเอง :)

หากจำเป็นให้ใช้ฟังก์ชั่น indexof สำหรับ Internet Explorer ตามที่อธิบายไว้ก่อนหน้าฉัน


1
สิ่งนี้จะทำงานกับสตริงตัวอักษรเดียวเท่านั้นซึ่งไม่ได้มีเจตนา
ErikE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.