Xác định xem chuỗi có trong danh sách trong JavaScript không


263

Trong SQL, chúng ta có thể thấy nếu một chuỗi nằm trong một danh sách như vậy:

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

Cách tốt để làm điều này trong JavaScript là gì? Thật khó để làm điều này:

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

Và tôi không chắc về hiệu suất hoặc sự rõ ràng của điều này:

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

Hoặc người ta có thể sử dụng chức năng chuyển đổi:

var str = 'a',
   flag = false;

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

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

Nhưng đó là một mớ hỗn độn khủng khiếp. Có ý kiến ​​gì không?

Trong trường hợp này, tôi phải sử dụng Internet Explorer 7 như một trang mạng nội bộ của công ty. Vì vậy, ['a', 'b', 'c'].indexOf(str) !== -1sẽ không hoạt động tự nhiên mà không có một số cú pháp đường.


1
Bạn có thể giải thích chính xác sự khác biệt giữa "chuỗi nằm trong danh sách" và "mảng bao gồm một đối tượng" không?
Michał Perłakowski

2
@Gothdo Vì một danh sách không phải lúc nào cũng là một mảng và một chuỗi không phải là một đối tượng? Làm thế nào nó có thể rõ ràng hơn?
ErikE

@ErikE nếu đây là trường hợp những gì bạn đã đề cập trong LƯU Ý thì câu hỏi này nên được đóng lại, không nên có thêm tiền thưởng / câu trả lời nào được cho phép. Câu trả lời đã được đăng là đủ cho bất cứ ai để có được sự giúp đỡ.
Vikasdeep Singh

@VicJordan Có lẽ bạn muốn xóa bình luận của mình vì nó không còn áp dụng.
ErikE

Câu trả lời:


280

Bạn có thể gọi indexOf:

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

15
Vấn đề duy nhất Array.prototype.indexOflà nó sẽ không hoạt động trên IE, đáng buồn là ngay cả IE8 cũng thiếu phương pháp này.
CMS

2
Nhưng bạn có thể định nghĩa nó nếu bạn muốn. Xem soledadpenades.com/2007/05/17/arrayindexof-in-iNET-explorer
Jason Hall

Dưới đây là hàng hóa "thực": developer.mozilla.org/en/Core_JavaScript_1.5_Reference/
Kẻ

8
@ImJasonH: Mã trên trang đó thực sự là IMHO thực sự xấu, ví dụ, việc kiểm tra nếu Array.indexOftồn tại trước khi trọng Array.prototype.indexOfkhông được điều tương tự. Tôi muốn giới thiệu việc triển khai bởi Mozilla có sẵn tại đây: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/
trộm

1
Nghe @CMS, @Emtucifor, việc triển khai Mozilla tốt hơn rất nhiều.
Hội trường Jason

230

EcmaScript 6

Nếu bạn đang sử dụng ES6, bạn có thể tạo một mảng các mục và sử dụng includes:

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

Điều này có một số lợi ích vốn có indexOfvì nó có thể kiểm tra chính xác sự hiện diện củaNaN trong danh sách, và có thể phù hợp với thiếu phần tử mảng như một trong những trung ở [1, , 2]đến undefined. includescũng hoạt động trên các mảng gõ JavaScript như Uint8Array.

Nếu bạn lo lắng về hỗ trợ trình duyệt (chẳng hạn như IE hoặc Edge), bạn có thể kiểm tra Array.includestại CanIUse.Com và nếu bạn muốn nhắm mục tiêu một trình duyệt hoặc phiên bản trình duyệt bị thiếu includes, tôi khuyên bạn nên sử dụng polyfill.io cho polyfill.

Không có một mảng

Bạn có thể thêm một thuộc tính mới isInListvào chuỗi như sau:

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

Sau đó sử dụng nó như vậy:

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

Bạn có thể làm điều tương tự cho Number.prototype.

Mảng.indexOf

Nếu bạn đang sử dụng một trình duyệt hiện đại, indexOf luôn hoạt động. Tuy nhiên, đối với IE8 trở về trước, bạn sẽ cần một polyfill.

Nếu indexOftrả về -1, mục này không có trong danh sách. Mặc dù vậy, hãy chú ý rằng phương pháp này sẽ không kiểm tra chính xác NaNvà trong khi nó có thể khớp với một phần tử rõ ràng undefined, thì nó không thể khớp với một phần tử bị thiếu undefinednhư trong mảng[1, , 2] .

Polyfill cho indexOfhoặcincludes trong IE hoặc bất kỳ trình duyệt / phiên bản nào khác thiếu hỗ trợ

Nếu bạn không muốn sử dụng một dịch vụ như polyfill.io như đã đề cập ở trên, bạn luôn có thể đưa vào các polyfill tùy chỉnh tuân thủ tiêu chuẩn mã nguồn của riêng bạn. Ví dụ: Mozilla Developer Network có một choindexOf .

Trong tình huống này khi tôi phải thực hiện một giải pháp cho Internet Explorer 7, tôi đã "tự lăn" phiên bản đơn giản hơn của indexOf() chức năng không tuân thủ tiêu chuẩn:

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

Tuy nhiên, tôi không nghĩ sửa đổi Array.prototypelà câu trả lời tốt nhất trong dài hạn. Sửa đổi ObjectArraynguyên mẫu trong JavaScript có thể dẫn đến các lỗi nghiêm trọng. Bạn cần phải quyết định xem làm như vậy có an toàn trong môi trường của chính bạn hay không. Lưu ý chính là việc lặp lại một mảng (khi Array.prototype có thêm thuộc tính) vớifor ... in sẽ trả về tên hàm mới là một trong các khóa:

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!

Mã của bạn có thể hoạt động ngay bây giờ, nhưng thời điểm ai đó trong tương lai thêm thư viện hoặc plugin JavaScript của bên thứ ba không thực sự bảo vệ chống lại các khóa được kế thừa, mọi thứ đều có thể bị phá vỡ.

Cách cũ để tránh sự phá vỡ đó là, trong quá trình liệt kê, kiểm tra từng giá trị để xem liệu đối tượng có thực sự là tài sản không được thừa kế với if (arr.hasOwnProperty(x))chỉ sau đó làm việc với nó khôngx .

Cách ES6 mới để tránh vấn đề phụ này là sử dụng ofthay vì in, for (let x of arr). Tuy nhiên, trừ khi bạn có thể đảm bảo rằng tất cả các thư viện mã và bên thứ ba của bạn tuân thủ nghiêm ngặt phương pháp này, thì với mục đích của câu hỏi này, có lẽ bạn sẽ chỉ muốn sử dụng includesnhư đã nêu ở trên.


2
đây là một tốt PolyFill cho indexOfcung cấp bởi MDN. Về cơ bản nó cũng làm điều tương tự nhưng với một vài mạch ngắn để dễ dàng đánh giá.
KyleMit

1
Downvoter: hãy bình luận. Có vấn đề gì với điều này? Chức năng này KHÔNG tuân thủ tiêu chuẩn và không cố gắng.
ErikE

Bạn nên sử dụng một cái gì đó đã được thử nghiệm như polyfill ghi nhận của liên kết @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
lmiguelmh

@lmiguelmh Làm thế nào về "polyfill" mà bản thân tôi đã đăng một liên kết đến, từ chính các tài liệu Mozilla? Trong mọi trường hợp, chức năng này rất đơn giản đến nỗi tôi thực sự không quá quan tâm đến việc thử nghiệm. Và bất cứ ai, không nên thực hiện chức năng "đã được thử nghiệm" mà nên tự kiểm tra bất cứ thứ gì họ sử dụng. Vì vậy, nhận xét của bạn là một chút thất lạc. Bạn đã xác định một khiếm khuyết cụ thể với chức năng của tôi?
ErikE

@ErikE bạn đúng chức năng này đã chết và bất cứ ai sử dụng câu trả lời của bạn đều biết. Tôi đã không thấy liên kết của bạn ở nơi đầu tiên bởi vì tôi đang tìm kiếm "câu trả lời"
lmiguelmh

53

Hầu hết các câu trả lời đề xuất Array.prototype.indexOfphương pháp, vấn đề duy nhất là nó sẽ không hoạt động trên bất kỳ phiên bản IE nào trước IE9.

Để thay thế, tôi để lại cho bạn hai tùy chọn khác sẽ hoạt động trên tất cả các trình duyệt:

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


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

Hmmm, cảm ơn vì đã đề cập đến điều đó, CMS. Nó chỉ xảy ra rằng đây là cho một mạng nội bộ của công ty và họ sử dụng ... đoán xem ... IE. Vì phương thức biểu thức chính quy cung cấp cho tôi các di chúc, tôi sẽ phải tạo một hàm lặp hoặc sử dụng phương thức đối tượng mà tôi đã đề xuất trong bài đăng của mình (khối mã thứ ba).
ErikE

13
Điều này sẽ phù hợp "HiFooThere"- /^(?:Foo|Bar|Baz)$/thay vào đó tôi sẽ đi cùng (bắt đầu chuỗi, nhóm không bắt, kết thúc chuỗi).
TrueWill

indexOfhiện được hỗ trợ trong IE 9 trở lên theo MDN .
krock

28

Mảng có một indexOfphương thức có thể được sử dụng để tìm kiếm chuỗi:

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

3
Điều này sẽ thất bại trên các trình duyệt cũ hơn.
eprebello

Rất tiếc ... đề cập rằng điều này không hoạt động trong IE sẽ rất tốt. :)
ErikE

2
@eprebello: Không chỉ trong các trình duyệt cũ hơn, nó sẽ thất bại trên mọi IE, ngay cả trong IE8 :(
CMS

12

Một mẹo tôi đã sử dụng là

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

Bạn có thể làm một cái gì đó như

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

chuyến đi, đang sử dụng "trong" bất kỳ nhanh hơn / chậm hơn / tốt hơn / tồi tệ hơn là chỉ cố gắng để quy định lại mục như khối mã thứ ba trong câu hỏi của tôi cho thấy? Ngoài ra, nếu bạn định lặp lại vấn đề, tôi cho rằng bạn cũng có thể kiểm tra xem phần tử có trong mảng tại thời điểm đó không ... chỉ cần bọc vòng lặp trong một hàm. Và đối với những gì nó có giá trị var i=a.length;while (i--) {/*use a[i]*/}là phương pháp vòng lặp nhanh nhất (nếu thứ tự ngược lại được chấp nhận).
ErikE

@Emtucifor: nó thực sự phụ thuộc vào những gì bạn đang làm và tôi đoán rằng nó có thể hoạt động khác nhau trên các công cụ javascript khác nhau. Nếu dữ liệu của bạn cần sử dụng từ điển, thì tốt hơn là tạo theo cách này. Tôi nghĩ rằng điều này sẽ nhanh hơn vì các chi tiết triển khai trên các công cụ (việc sử dụng bảng băm cho đối tượng) một khi đối tượng dict được tạo .
Esteban Küber

Trong JavaScript, nó không được gọi là dict, nó được gọi là một đối tượng .
Solomon Ucko

8

Đây là của tôi:

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

Cái này nhanh hơn nếu bạn đồng ý với việc truyền một mảng:

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

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

Đây là jsperf: http://jsperf.com/bmcgin-inlsit


Thành thật mà nói, một trong những nơi bạn vượt qua một mảng có thể sẽ hữu ích hơn, và nó có lẽ nên là trên Arraynguyên mẫu: có thể là một cái gì đó như thế Array.prototype.contains.
Solomon Ucko

6

RegExplà phổ quát, nhưng tôi hiểu rằng bạn đang làm việc với mảng. Vì vậy, hãy kiểm tra phương pháp này. Tôi sử dụng để sử dụng nó, và nó rất hiệu quả và nhanh chóng!

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

rx.test(str);

Bạn cũng có thể áp dụng một số sửa đổi, nghĩa là:

Lót

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

Trường hợp không nhạy cảm

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


Và nhiều người khác!


Sử dụng regex yêu cầu tránh một loạt các ký tự đặc biệt. Nó cũng ít rõ ràng hơn. Tôi không nghĩ đó là một giải pháp tốt.
ErikE 6/03/2015

@ErikE Tôi hiểu các đặt phòng của bạn vì vậy tôi đã cập nhật câu trả lời của mình bằng cách sử dụng một mã khác không sử dụng RegExp, nó tương thích với IE, rất đơn giản và nhanh chóng.
sospedra

Mã bổ sung của bạn gần như trùng lặp với câu trả lời của tôi mà tôi đã cung cấp 5 năm trước khi bạn quyết định đăng giải pháp Regex. Cảm ơn bạn đã tham gia, đồng thời tôi nghĩ quay trở lại câu trả lời trước của bạn là tốt nhất.
ErikE

@ErikE yep, bạn nói đúng, tôi không sử dụng để kiểm tra câu trả lời trong câu hỏi. Và tôi đồng ý rằng nó thực sự tương tự.
sospedra

6

Sử dụng indexOf (nó không hoạt động với IE8).

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

Để hỗ trợ IE8, bạn có thể triển khai indexOf của Mozilla.

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

Biểu thức chính quy thông qua String.prototype.match (tài liệu).

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

}

1
Bạn có nhận thấy rằng bạn đang sao chép chính xác các câu trả lời khác trên trang không? Điều này không thêm bất kỳ giá trị.
ErikE

5

Có vẻ như bạn cần sử dụng hàm in_array.

jQuery -> inArray

Nguyên mẫu -> Array.indexOf

Hoặc, xem các ví dụ này nếu bạn không sử dụng jQuery hoặc Prototype:

Ghi chú về phong cách: các biến có tên là thisthing thatthing, nên được đặt tên để cho bạn biết điều gì đó về những gì chúng chứa (danh từ).


Ồ, chúng không phải là biến nhưng có nghĩa là giữ chỗ ngẫu nhiên cho các biểu thức ... chỉ là một ví dụ về cách tôi dự định sử dụng tập lệnh.
ErikE

5

Ngoài indexOf(mà các áp phích khác đã đề xuất), sử dụng Enumerable.include () của nguyên mẫu có thể làm cho điều này gọn gàng và súc tích hơn:

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

5
Enumerable.include () đã không được chấp nhận, nhưng Array.prototype.includes () đang đến và đã hoạt động trong hầu hết các trình duyệt trừ IE (tất nhiên) và Edge (tất nhiên).
ngựa trắng

2

Cảm ơn câu hỏi và giải pháp sử dụng phương thức Array.indexOf.

Tôi đã sử dụng mã từ giải pháp này để tạo hàm inList (), IMO, giúp việc viết đơn giản hơn và đọc rõ ràng hơn:

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

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

SỬ DỤNG:

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

1
Các mảng chữ Javascript rất dễ, tôi không hiểu tại sao bạn lại chia ra khi bạn có thể làm 'Houston'.inList(['LA', 'New York', 'Houston']). Có lẽ if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}hoặc sử dụng whilephương pháp của bạn .
ErikE

0

Tôi ngạc nhiên không ai đã đề cập đến một chức năng đơn giản có một chuỗi và một danh sách.

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

Jim đã làm chính xác những gì bạn đề xuất , Sam, vào ngày 27 tháng 8 năm 11 lúc 1:29. Trong thực tế, câu trả lời được chọn của tôi khá giống nhau, chỉ cung cấp chuỗi này chứ không phải là một tham số.
ErikE

@ErikE Xin lỗi, câu trả lời của Jims có vẻ kỳ lạ với tôi như bạn đã nói. Và câu trả lời được chấp nhận của bạn trả về một int, trong đó một số người có thể bắt gặp câu hỏi này để tìm kiếm sự booltrở lại. Hình nó có thể giúp một vài người.
Samuel Parkinson

0

Giải pháp của tôi dẫn đến một cú pháp như thế này:

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

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

Và tôi thực hiện điều này bằng cách mở rộng nguyên mẫu Object cơ bản, như thế này:

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

Chúng ta có thể đặt tên khác cho phương thức làInArray (nhưng có lẽ không phải là inArray) hoặc đơn giản là isIn.

Ưu điểm: Đơn giản, dễ hiểu và tự ghi chép.


Có thể có vấn đề mở rộng đối tượng. bolinfest.com/javascript/inherribution.php trong "Nhóm Google Maps đã học được điều này một cách khó khăn" và không tương thích với việc triển khai trình duyệt hoặc mã người dùng khác. Tôi vẫn nghĩ rằng câu trả lời của ErikE là câu trả lời hay nhất vì việc lặp qua một mảng chậm hơn so với việc tìm khóa trong hàm băm sau khi hashmap được tạo: myValues[key];trong đó myValues ​​là một đối tượng và khóa là bất kỳ chuỗi hoặc số nào.
HMR

-1

Một phiên bản đơn giản hóa câu trả lời của SLaks cũng hoạt động:

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

.... vì các chuỗi là chính các mảng. :)

Nếu cần, hãy triển khai chức năng indexof cho Internet Explorer như được mô tả trước tôi.


1
Điều này sẽ chỉ hoạt động với các chuỗi ký tự đơn, không được dự định.
ErikE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.