Tentukan apakah string ada dalam daftar di JavaScript

263

Dalam SQL kita dapat melihat apakah sebuah string ada dalam daftar seperti ini:

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

Apa cara yang baik untuk melakukan ini dalam JavaScript? Sangat kikuk untuk melakukan ini:

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

Dan saya tidak yakin dengan kinerja atau kejelasan ini:

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

Atau seseorang dapat menggunakan fungsi sakelar:

var str = 'a',
   flag = false;

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

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

Tapi itu berantakan sekali. Ada ide?

Dalam hal ini, saya harus menggunakan Internet Explorer 7 karena itu untuk halaman intranet perusahaan. Jadi ['a', 'b', 'c'].indexOf(str) !== -1tidak akan bekerja secara asli tanpa gula sintaks.

ErikE
sumber
1
Bisakah Anda menjelaskan apa sebenarnya perbedaan antara "string ada di daftar" dan "array termasuk objek"?
Michał Perłakowski
2
@ Haruskah Karena daftar tidak selalu berupa array, dan string bukan objek? Bagaimana bisa lebih jelas?
ErikE
@ErikE jika ini kasus yang Anda sebutkan di CATATAN maka pertanyaan ini harus ditutup, tidak boleh ada karunia / jawaban lebih lanjut diizinkan. Jawaban yang sudah diposting cukup bagi siapa saja untuk mendapatkan bantuan.
Vikasdeep Singh
@VicJordan Mungkin Anda ingin menghapus komentar Anda karena tidak lagi berlaku.
ErikE

Jawaban:

280

Anda dapat menghubungi indexOf:

if (['a', 'b', 'c'].indexOf(str) >= 0) {
    //do something
}
Slaks
sumber
15
Satu-satunya masalah Array.prototype.indexOfadalah bahwa itu tidak akan berfungsi pada IE, sayangnya bahkan IE8 tidak memiliki metode ini.
CMS
2
Tetapi Anda dapat mendefinisikannya jika Anda mau. Lihat soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer
Jason Hall
8
@ImJasonH: Kode pada halaman yang benar-benar IMHO benar-benar buruk, misalnya, memeriksa apakah Array.indexOfada sebelum override Array.prototype.indexOfyang tidak hal yang sama. Saya akan merekomendasikan implementasi yang dibuat oleh Mozilla tersedia di sini: developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/…
CMS
1
Dengarkan @CMS, @Emtucifor, implementasi Mozilla jauh lebih baik.
Jason Hall
231

EcmaScript 6

Jika Anda menggunakan ES6, Anda bisa membuat array item, dan menggunakan includes:

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

Ini memiliki beberapa manfaat yang melekat lebih indexOfkarena benar dapat menguji keberadaan dari NaNdalam daftar, dan dapat mencocokkan hilang elemen array seperti yang tengah di [1, , 2]untuk undefined. includesjuga berfungsi pada array yang diketikkan JavaScript seperti Uint8Array.

Jika Anda khawatir tentang dukungan browser (seperti untuk IE atau Edge), Anda dapat memeriksa Array.includesdi CanIUse.Com , dan jika Anda ingin menargetkan versi browser atau browser yang hilang includes, saya sarankan polyfill.io untuk pengisian polyfilling.

Tanpa Array

Anda bisa menambahkan isInListproperti baru ke string sebagai berikut:

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

Kemudian gunakan seperti ini:

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

Anda dapat melakukan hal yang sama untuk Number.prototype.

Array.indexOf

Jika Anda menggunakan browser modern, indexOfselalu berfungsi. Namun, untuk IE8 dan sebelumnya Anda akan memerlukan polyfill.

Jika indexOfmengembalikan -1, item tidak ada dalam daftar. Berhati-hatilah, bahwa metode ini tidak akan memeriksa dengan benar NaN, dan sementara itu bisa cocok dengan eksplisit undefined, itu tidak dapat mencocokkan elemen yang hilang undefinedseperti dalam array [1, , 2].

Polyfill untuk indexOfatau includesdi IE, atau browser / versi lain yang tidak memiliki dukungan

Jika Anda tidak ingin menggunakan layanan seperti polyfill.io seperti yang disebutkan di atas, Anda selalu dapat memasukkan dalam polyfill khusus sesuai standar kode sumber Anda. Misalnya, Jaringan Pengembang Mozilla memiliki satu untuk indexOf.

Dalam situasi ini di mana saya harus membuat solusi untuk Internet Explorer 7, saya "menggulirkan versi saya sendiri" dari indexOf()fungsi yang tidak sesuai standar:

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

Namun, saya tidak berpikir memodifikasi Array.prototypeadalah jawaban terbaik dalam jangka panjang. Memodifikasi Objectdan membuat Arrayprototipe dalam JavaScript dapat menyebabkan bug serius. Anda perlu memutuskan apakah melakukannya aman di lingkungan Anda sendiri. Dari catatan utama adalah bahwa iterasi array (ketika Array.prototype telah menambahkan properti) dengan for ... inakan mengembalikan nama fungsi baru sebagai salah satu kunci:

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!

Kode Anda mungkin berfungsi sekarang, tetapi saat seseorang di masa depan menambahkan pustaka JavaScript atau plugin pihak ketiga yang tidak dengan bersemangat melindungi terhadap kunci yang diwarisi, semuanya dapat rusak.

Cara lama untuk menghindari kerusakan itu adalah, selama enumerasi, untuk memeriksa setiap nilai untuk melihat apakah objek tersebut benar-benar memilikinya sebagai properti yang tidak diwariskan dengan if (arr.hasOwnProperty(x))dan baru kemudian bekerja dengannya x.

Cara ES6 baru untuk menghindari masalah ekstra-tombol ini adalah dengan menggunakan ofbukan in, for (let x of arr). Namun, kecuali Anda dapat menjamin bahwa semua kode dan pustaka pihak ketiga Anda benar-benar berpegang pada metode ini, maka untuk keperluan pertanyaan ini Anda mungkin hanya ingin menggunakan includesseperti yang disebutkan di atas.

ErikE
sumber
2
inilah PolyFill yang bagus untuk indexOfdisediakan oleh MDN. Ini pada dasarnya melakukan hal yang sama tetapi dengan beberapa hubung singkat untuk evaluasi mudah.
KyleMit
1
Downvoter: tolong beri komentar. Apa masalahnya dengan ini? Fungsi ini BUKAN sesuai standar, dan tidak berusaha untuk menjadi.
ErikE
Anda harus menggunakan sesuatu yang sudah diuji seperti polyfill yang dicatat oleh tautan @KyleMit: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lmiguelmh
@lmiguelmh Bagaimana dengan "polyfill" yang saya sendiri kirim tautannya, dari dokumen Mozilla sendiri? Bagaimanapun, fungsi ini sangat sederhana sehingga saya benar-benar tidak terlalu khawatir tentang pengujian. Dan siapa pun yang, tidak boleh mengambil fungsi "sudah diuji" tetapi harus menguji apa pun yang mereka gunakan, sendiri. Jadi komentar Anda agak salah tempat. Sudahkah Anda mengidentifikasi cacat tertentu dengan fungsi saya?
ErikE
@ErikE Anda benar, fungsi ini mati dan siapa pun yang menggunakan jawaban Anda harus mengetahuinya. Saya tidak melihat tautan Anda sejak awal karena saya mencari "jawaban"
lmiguelmh
53

Sebagian besar jawaban menyarankan Array.prototype.indexOfmetode, satu-satunya masalah adalah bahwa itu tidak akan berfungsi pada versi IE apa pun sebelum IE9.

Sebagai alternatif, saya meninggalkan Anda dua opsi lagi yang akan berfungsi di semua browser:

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


if (str.match("Foo|Bar|Baz")) {
  // ...
}
CMS
sumber
Hmmm, terima kasih sudah menyebutkan itu, CMS. Kebetulan ini untuk intranet perusahaan dan mereka menggunakan ... coba tebak ... IE. Karena metode ekspresi reguler memberi saya tekad, saya harus membuat fungsi yang loop, atau menggunakan metode objek yang saya sarankan dalam posting saya (blok kode ketiga).
ErikE
13
Ini akan cocok "HiFooThere"- Saya akan menggunakan /^(?:Foo|Bar|Baz)$/(mulai dari string, grup yang tidak menangkap, akhir string).
TrueWill
indexOfsekarang didukung di IE 9 dan di atas sesuai dengan MDN .
Krock
28

Array memiliki indexOfmetode yang dapat digunakan untuk mencari string:

js> a = ['foo', 'bar', 'baz']
foo,bar,baz
js> a.indexOf('bar')
1
js> a.indexOf('quux')
-1
harto
sumber
3
Ini akan gagal pada browser lama.
epascarello
Ups ... menyebutkan bahwa ini tidak berfungsi di IE akan lebih baik. :)
ErikE
2
@epascarello: Tidak hanya di browser lama, itu akan gagal pada IE apa pun , bahkan di IE8 :(
CMS
12

Trik yang saya gunakan adalah

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

Anda bisa melakukan sesuatu seperti

>>> 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
Esteban Küber
sumber
voyager, apakah menggunakan "dalam" lebih cepat / lebih lambat / lebih baik / lebih buruk daripada hanya mencoba untuk mengubah referensi item seperti yang ditunjukkan oleh blok kode ketiga dalam pertanyaan saya? Juga, jika Anda akan mengulangi hal itu, saya pikir Anda mungkin juga memeriksa apakah elemen dalam array pada waktu itu ... hanya membungkus loop dalam suatu fungsi. Dan untuk apa nilainya var i=a.length;while (i--) {/*use a[i]*/}adalah metode loop tercepat (jika urutan terbalik diterima).
ErikE
@ Emtucifor: itu benar-benar tergantung pada apa yang Anda lakukan, dan saya kira itu mungkin bekerja secara berbeda pada mesin javascript yang berbeda. Jika suatu saat data Anda perlu menggunakan kamus, maka lebih baik membuatnya dengan cara ini. Saya akan berpikir bahwa ini akan lebih cepat karena detail implementasi pada mesin (penggunaan tabel hash untuk objek) setelah objek dict dibuat .
Esteban Küber
Dalam JavaScript, ini tidak disebut dict, itu disebut objek .
Solomon Ucko
8

Ini milik saya:

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

Yang ini lebih cepat jika Anda setuju dengan melewatkan array:

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

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

Inilah jsperf: http://jsperf.com/bmcgin-inlsit

Brian McGinity
sumber
Terus terang, di mana Anda melewatkan array mungkin akan lebih bermanfaat, dan mungkin harus Arraydi prototipe: mungkin sesuatu seperti Array.prototype.contains.
Solomon Ucko
6

RegExpbersifat universal, tetapi saya mengerti bahwa Anda bekerja dengan array. Jadi, periksa pendekatan ini. Saya menggunakannya untuk menggunakannya, dan ini sangat efektif dan sangat cepat!

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

rx.test(str);

Anda juga dapat menerapkan beberapa modifikasi, yaitu:

Satu garis

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

Tidak sensitif huruf

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


Dan banyak lagi!

sospedra
sumber
Menggunakan regex harus menghindari banyak karakter khusus. Itu juga kurang jelas. Saya pikir itu bukan solusi yang baik.
ErikE
@ErikE Saya mengerti pemesanan Anda, jadi saya memperbarui jawaban saya dengan menambahkan kode lain yang tidak menggunakan RegExp, ini sangat kompatibel dengan IE, sangat idiomatis dan cepat.
sospedra
Kode tambahan Anda hampir merupakan duplikat dari jawaban saya yang saya berikan 5 tahun sebelum Anda memutuskan untuk mengirim solusi Regex. Terima kasih telah berpartisipasi, dan pada saat yang sama saya pikir kembali ke jawaban Anda sebelumnya adalah yang terbaik.
ErikE
@ErikE ya, Anda benar, saya tidak menggunakan untuk memeriksa jawaban dalam pertanyaan. Dan saya setuju itu sangat mirip.
sospedra
6

Menggunakan indexOf (tidak berfungsi dengan IE8).

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

Untuk mendukung IE8, Anda dapat menerapkan indexOf dari Mozilla.

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

Ekspresi Reguler melalui String.prototype.match (docs).

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

}
Tiago Medici
sumber
1
Apakah Anda memperhatikan bahwa Anda persis menggandakan jawaban lain pada halaman? Ini tidak menambah nilai apa pun.
ErikE
5

Sepertinya Anda perlu menggunakan fungsi in_array.

jQuery -> inArray

Prototipe -> Array.indexOf

Atau, lihat contoh ini jika Anda tidak menggunakan jQuery atau Prototype:

Stylistic note: variabel bernama thisthing thatthing, harus dinamai untuk memberi tahu Anda sesuatu tentang apa yang dikandungnya (kata benda).

LG_PDX
sumber
Oh, mereka bukan variabel tetapi dimaksudkan sebagai pengganti acak untuk ekspresi ... hanya contoh bagaimana saya berencana menggunakan skrip.
ErikE
5

Selain indexOf(yang disarankan oleh poster lain), menggunakan prototipe's Enumerable.include () dapat membuatnya lebih rapi dan ringkas:

var list = ['a', 'b', 'c'];
if (list.include(str)) {
  // do stuff
}
pkaeding
sumber
5
Enumerable.include () telah usang, tetapi Array.prototype.includes () akan datang, dan sudah berfungsi di sebagian besar browser kecuali IE (tentu saja) dan Edge (tentu saja).
whitebeard
2

Terima kasih atas pertanyaannya, dan solusinya menggunakan metode Array.indexOf.

Saya menggunakan kode dari solusi ini untuk membuat fungsi inList () yang akan, IMO, membuat penulisan lebih mudah dan pembacaan menjadi lebih jelas:

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

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

PEMAKAIAN:

if (inList('Houston', 'LA,New York,Houston') {
  // THEN do something when your string is in the list
}
JMichaelTX
sumber
1
Literatur array Javascript sangat mudah, saya tidak melihat mengapa Anda akan membagi ketika Anda bisa melakukannya 'Houston'.inList(['LA', 'New York', 'Houston']). Mungkin if (!String.prototype.inList) {String.prototype.inList = function(arr) {return arr.indexOf(this) >= 0};}atau menggunakan whilemetode Anda .
ErikE
0

Saya terkejut tidak ada yang menyebutkan fungsi sederhana yang mengambil string dan daftar.

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));
Samuel Parkinson
sumber
Jim melakukan persis apa yang Anda sarankan , Sam, pada 27 Agustus '11 pada 1:29. Sebenarnya, jawaban yang saya pilih adalah hal yang hampir sama, hanya memasok string dengan ini daripada parameter.
ErikE
@ErikE Maaf, jawaban Jims tampak aneh bagi saya seperti yang Anda katakan. Dan jawaban yang Anda terima mengembalikan sebuah int, di mana sebagian orang mungkin akan menemukan pertanyaan ini mencari boolimbalan. Kupikir itu mungkin membantu beberapa orang.
Samuel Parkinson
0

Solusi saya menghasilkan sintaks seperti ini:

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

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

Dan saya menerapkan ini dengan memperluas prototipe Objek dasar, seperti ini:

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

Kita mungkin menamai metode ini dengan InArray (tapi mungkin bukan inArray) atau cukup dengan In.

Keuntungan: Sederhana, lugas, dan mendokumentasikan diri.

John Hicks
sumber
Mungkin ada masalah saat memperpanjang Object. bolinfest.com/javascript/inheritance.php di bawah "Tim Google Maps mempelajari ini dengan cara yang sulit" dan ketidakcocokan dengan implementasi browser atau kode pengguna lain. Saya masih berpikir jawaban ErikE adalah yang terbaik karena iterasi pada array lebih lambat daripada menemukan kunci dalam hashmap setelah hashmap dibuat: di myValues[key];mana myValues ​​adalah objek dan kunci adalah string atau angka.
HMR
-1

Versi sederhana dari jawaban SLaks juga berfungsi:

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

.... karena string adalah semacam array itu sendiri. :)

Jika perlu, terapkan fungsi indexof untuk Internet Explorer seperti yang dijelaskan sebelumnya.

Angezerus
sumber
1
Ini hanya akan berfungsi dengan string huruf tunggal, yang TIDAK dimaksudkan.
ErikE