Bagaimana mempersingkat pernyataan bersyarat saya

154

Saya memiliki pernyataan kondisional yang sangat panjang seperti berikut:

if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
    // do something.
}

Saya bertanya-tanya apakah saya bisa mengubah ekspresi / pernyataan ini menjadi bentuk yang lebih ringkas.

Adakah cara untuk mencapai ini?

Kucing Terbang
sumber
23
Anda bisa meletakkannya di dalam array dan menggunakannya in?
jeremy
sekarang hanya jika seseorang dapat memeriksa mana yang tercepat
Muhammad Umer
3
ini mungkin mengejutkan bagi semua orang, tetapi apa yang dimiliki OP adalah pemenang yang jelas dalam kecepatan !!!!!!! Mungkin menyebabkan banyak browser mengoptimalkan ini .. Hasil: (1) jika dengan ||. (2) switchpernyataan. (3) regex. (4) ~. jsperf.com/if-statements-test-techsin
Muhammad Umer
3
Anda mungkin juga mendekati ini dengan cara yang salah. Dalam hal ini, keempat tipe tersebut memiliki kesamaan. Apa itu? Jika kita membawa ini ke kasus yang lebih ekstrim, bagaimana jika kita perlu menambahkan 10 jenis lagi agar sesuai dengan kondisi ini. Atau 100? Jika ada lebih banyak, Anda mungkin tidak akan mempertimbangkan untuk menggunakan solusi ini, atau yang lain menyarankan. Anda melihat pernyataan if besar seperti ini, dan berpikir itu bau kode, yang merupakan pertanda baik. Cara terbaik Anda untuk membuat ini lebih ringkas adalah jika Anda bisa menulis if (test.your_common_condition). Lebih mudah dipahami dalam konteks ini, dan lebih bisa diperluas.
gmacdougall

Jawaban:

241

Masukkan nilai Anda ke dalam array, dan periksa apakah item Anda ada di dalam array:

if ([1, 2, 3, 4].includes(test.type)) {
    // Do something
}

Jika browser yang Anda dukung tidak memiliki Array#includesmetode, Anda dapat menggunakan polyfill ini .


Penjelasan singkat tentang ~pintasan tilde:

Pembaruan: Karena kita sekarang memiliki includesmetode, tidak ada gunanya menggunakan ~peretasan lagi. Hanya menyimpannya di sini untuk orang-orang yang tertarik mengetahui cara kerjanya dan / atau menjumpainya dalam kode orang lain.

Alih-alih memeriksa jika hasilnya dari indexOfadalah >= 0, ada jalan pintas kecil yang menyenangkan:

if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
    // Do something
}

Ini biola: http://jsfiddle.net/HYJvK/

Bagaimana cara kerjanya? Jika item ditemukan dalam array, indexOfkembalikan indeksnya. Jika item itu tidak ditemukan, itu akan kembali -1. Tanpa terlalu banyak detail, ~ini adalah operator BUKAN bitwise , yang 0hanya akan kembali untuk -1.

Saya suka menggunakan jalan ~pintas, karena lebih ringkas daripada melakukan perbandingan pada nilai kembali. Saya berharap JavaScript akan memiliki in_arrayfungsi yang mengembalikan Boolean secara langsung (mirip dengan PHP), tapi itu hanya angan-angan ( Pembaruan: sekarang. Ini disebut includes. Lihat di atas). Perhatikan bahwa jQuery's inArray, saat membagikan tanda tangan metode PHP, sebenarnya meniru indexOffungsionalitas asli (yang berguna dalam kasus yang berbeda, jika indeksnya benar-benar setelah Anda).

Catatan penting: Menggunakan pintasan tilde tampaknya dibalut kontroversi, karena beberapa orang sangat percaya bahwa kode tidak cukup jelas dan harus dihindari dengan cara apa pun (lihat komentar pada jawaban ini). Jika Anda berbagi sentimen mereka, Anda harus tetap berpegang pada .indexOf(...) >= 0solusinya.


Penjelasan lebih lama:

Integer dalam JavaScript ditandatangani, yang berarti bahwa bit paling kiri dicadangkan sebagai bit tanda; sebuah bendera untuk menunjukkan apakah angka itu positif atau negatif, dengan 1yang negatif.

Berikut adalah beberapa contoh angka positif dalam format biner 32-bit:

1 :    00000000000000000000000000000001
2 :    00000000000000000000000000000010
3 :    00000000000000000000000000000011
15:    00000000000000000000000000001111

Sekarang ini adalah angka-angka yang sama, tetapi negatif:

-1 :   11111111111111111111111111111111
-2 :   11111111111111111111111111111110
-3 :   11111111111111111111111111111101
-15:   11111111111111111111111111110001

Mengapa kombinasi aneh seperti itu untuk angka negatif? Sederhana. Angka negatif hanyalah kebalikan dari angka positif +1; menambahkan angka negatif ke angka positif harus selalu menghasilkan 0.

Untuk memahami ini, mari kita lakukan aritmatika biner sederhana.

Berikut adalah bagaimana kita akan menambahkan -1ke +1:

   00000000000000000000000000000001      +1
+  11111111111111111111111111111111      -1
-------------------------------------------
=  00000000000000000000000000000000       0

Dan di sini adalah bagaimana kita akan menambahkan -15ke +15:

   00000000000000000000000000001111      +15
+  11111111111111111111111111110001      -15
--------------------------------------------
=  00000000000000000000000000000000        0

Bagaimana kita mendapatkan hasil itu? Dengan melakukan penambahan reguler, cara kami diajarkan di sekolah: Anda mulai di kolom paling kanan, dan Anda menambahkan semua baris. Jika jumlahnya lebih besar dari angka satu digit terbesar (yang dalam desimal 9, tetapi dalam biner 1), kami membawa sisanya ke kolom berikutnya.

Sekarang, seperti yang akan Anda perhatikan, ketika menambahkan angka negatif ke angka positifnya, kolom paling kanan yang tidak semua 0s akan selalu memiliki dua 1s, yang bila ditambahkan bersama akan menghasilkan 2. Representasi biner dari dua wujud 10, kita bawa 1ke kolom berikutnya, dan beri 0hasil pada kolom pertama. Semua kolom lain di sebelah kiri hanya memiliki satu baris dengan 1, sehingga yang 1terbawa dari kolom sebelumnya akan bertambah lagi 2, yang kemudian akan terbawa ... Proses ini berulang sampai kita sampai ke kolom paling kiri, di mana yang 1akan dibawa ke mana-mana, sehingga meluap dan hilang, dan kami pergi dengan 0s di seluruh.

Sistem ini disebut 2's Complement . Anda dapat membaca lebih lanjut tentang ini di sini:

Representasi Pelengkap 2 untuk Integer yang Ditandatangani .


Sekarang kursus kilat di komplemen 2 sudah berakhir, Anda akan melihat bahwa -1hanya nomor yang representasi binernya ada 1di seluruh.

Menggunakan ~operator bitwise NOT, semua bit dalam jumlah yang diberikan dibalik. Satu - satunya cara untuk 0kembali dari membalikkan semua bit adalah jika kita mulai dengan 1semua.

Jadi, semua ini adalah cara yang panjang lebar untuk mengatakan bahwa ~nhanya akan kembali 0jika nada -1.

Joseph Silber
sumber
59
Meskipun menggunakan operator bitwise benar-benar seksi, apakah ini benar-benar lebih baik daripada !== -1cara apa pun yang mungkin? Bukankah logika boolean eksplisit lebih tepat daripada secara implisit menggunakan kepalsuan nol?
Phil
21
Teknisi yang bagus, tapi saya tidak suka itu. Pada pandangan pertama tidak jelas apa yang dilakukan kode tersebut, yang membuatnya tidak dapat dipelihara. Saya lebih suka jawaban "Yuriy Galanter".
Jon Rea
65
-1 programmer baru melihat jawaban seperti ini, berpikir itu adalah cara pengkodean yang keren dan dapat diterima, maka dalam 5 tahun saya harus mempertahankan kode mereka dan merobek rambut saya
BlueRaja - Danny Pflughoeft
23
Ungkapan ini jelas tidak umum dalam bahasa seperti C #, Java, atau Python, yang merupakan bidang keahlian saya. Dan saya hanya bertanya kepada beberapa ahli Javascript lokal di sini, dan tidak ada dari mereka yang pernah melihatnya sebelumnya; jadi itu jelas tidak biasa seperti yang Anda klaim. Itu harus selalu dihindari demi yang lebih jelas dan lebih umum != -1.
BlueRaja - Danny Pflughoeft
12
-1 karena bitfiddling yang tidak perlu dalam bahasa yang tidak terlalu menentukan tentang representasi bit. Plus, sebagian besar dari jawaban ini adalah menjelaskan bitfiddling. Jika Anda harus menulis 20 paragraf untuk menjelaskan peretasan, apakah BENAR-BENAR menghemat waktu?
halus
242

Anda dapat menggunakan pernyataan beralih dengan jatuh melalui:

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}
Yuriy Galanter
sumber
9
pada dasarnya sama dengan jika, indeks metode array jauh lebih baik
NimChimpsky
6
@ Kojiro sayangnya, jawaban yang tepat adalah ini, tetapi tidak mungkin untuk mendapatkan perhatian pada ini daripada trik bitwhise-array yang mengagumkan.
Manu343726
1
Saya tahu jawaban ini harus ada di sini, tetapi saya harus menggulir ke bawah untuk menemukannya. Persis seperti yang dirancang untuk pernyataan peralihan dan dibawa ke banyak bahasa lain. Saya telah menemukan banyak orang tidak tahu tentang metode 'jatuh melalui' dalam pernyataan pergantian.
Jimmy Johnson
3
Solusi ini adalah yang tercepat di Firefox dan Safari dan tercepat kedua (setelah yang asli ||) di Chrome. Lihat jsperf.com/if-statements-test-techsin
pabouk
3
Saya pikir ini akan mengerikan untuk menulis dalam suatu metode jika Anda memiliki banyak kondisi ... Itu baik untuk beberapa kondisi, tetapi untuk lebih dari 10 saya akan pergi untuk array agar kode tetap rapi.
yu_ominae
63

Menggunakan Ilmu: Anda harus melakukan apa yang idfah katakan dan ini untuk kecepatan tercepat sambil menjaga kode pendek:

INI LEBIH CEPAT DARI ~METODE

var x = test.type;
if (x == 'itema' ||
    x == 'itemb' ||
    x == 'itemc' ||
    x == 'itemd') {
    //do something
}

http://jsperf.com/if-statements-test-techsin masukkan deskripsi gambar di sini (Perangkat teratas: Chrome, perangkat bawah: Firefox)

Kesimpulan:

Jika kemungkinan yang beberapa dan Anda tahu bahwa orang-orang tertentu lebih mungkin terjadi daripada yang Anda dapatkan performa maksimal if ||, switch fall through, dan if(obj[keyval]).

Jika kemungkinannya banyak , dan siapa pun di antara mereka yang paling mungkin terjadi, dengan kata lain, Anda tidak bisa tahu mana yang paling mungkin terjadi daripada Anda mendapatkan kinerja terbanyak dari pencarian objek if(obj[keyval])dan regexjika itu cocok.

http://jsperf.com/if-statements-test-techsin/12

Saya akan memperbarui jika sesuatu yang baru muncul.

Muhammad Umer
sumber
2
+1 untuk pos yang sangat bagus! Jika saya mengerti dengan benar, switch caseapakah metode tercepat?
user1477388
1
di firefox ya, di chrome-nyaif ( ...||...||...)...
Muhammad Umer
8
Lebih cepat jika Anda benar-benar melakukan banyak loop atas input ini, tetapi jauh lebih lambat jika Anda memiliki satu loop dengan n yang sangat besar (jumlah string "itemX"). Saya meretas pembuat kode ini yang dapat Anda gunakan untuk memverifikasi (atau mungkin menyangkal). obj["itemX"]sangat cepat jika n besar. Pada dasarnya, apa yang cepat tergantung pada konteks. Selamat bersenang-senang.
kojiro
3
Jadi ini metode tercepat, tetapi apakah itu penting ?
congusbongus
1
@Mich Jangan mengorbankan keanggunan kode hanya untuk kecepatan. Inilah yang akan dikatakan banyak orang kepada Anda. Pada akhirnya, gunakan akal sehat saja.
Andre Figueiredo
32

Jika Anda membandingkan dengan string dan ada pola, pertimbangkan untuk menggunakan ekspresi reguler.

Kalau tidak, saya curiga mencoba memperpendeknya hanya akan mengaburkan kode Anda. Pertimbangkan hanya membungkus garis untuk membuatnya cantik.

if (test.type == 'itema' ||
    test.type == 'itemb' ||
    test.type == 'itemc' ||
    test.type == 'itemd') {
    do something.
}
idfah
sumber
4
jawaban ini adalah pemenang dalam hal kecepatan jsperf.com/if-statements-test-techsin
Muhammad Umer
1
Ini juga yang paling mudah untuk diperluas ketika proyek masuk ke mode pemeliharaan (dengan aturan seperti, (test.type == 'itemf' && foo.mode == 'detailed'))
Izkata
16
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) {  }

Menggunakan objek sebagai array asosiatif adalah hal yang cukup umum, tetapi karena JavaScript tidak memiliki set asli, Anda dapat menggunakan objek sebagai set murah juga.

kojiro
sumber
Bagaimana ini lebih pendek dari biasanya jika pernyataan bahwa FlyingCat sedang mencoba untuk mempersingkat?
dcarson
1
Bersyarat ifpernyataan @dcarson OP membutuhkan 78 karakter jika Anda menghapus semua spasi putih. Tambang membawa 54 jika Anda menulis seperti ini: test.type in {"itema":1,"itemb":1,"itemc":1,"itemd":1}. Pada dasarnya, ia menggunakan empat karakter untuk setiap dua penggunaan tambang untuk setiap kunci tambahan.
kojiro
1
tetapi Anda dapat melakukan: jika (kemungkinan [test.type]) dan menyimpan 2 karakter secara keseluruhan! :)
dc5
15
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

atau jika barangnya tidak seragam, maka:

if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }
Mat
sumber
9
"Beberapa orang, ketika dihadapkan dengan masalah, berpikir 'Saya tahu, saya akan menggunakan ekspresi reguler.' Sekarang mereka memiliki dua masalah. " - Jamie Zawinski, 1997
Moshe Katz
5
@MosheKatz Meskipun saya bisa mengerti orang-orang suka bandy tentang kutipan itu - dan orang-orang tentu saja menggunakan ekspresi reguler untuk hal-hal yang sama sekali tidak cocok, tetapi ini bukan salah satu dari mereka. Dalam kasus yang disediakan oleh OP, ini tidak hanya cocok dengan kriteria, tetapi juga sangat baik. Ekspresi reguler tidak secara inheren jahat, dan string yang cocok dengan parameter yang didefinisikan dengan baik adalah tujuan dibuatnya.
Thor84no
3
@ Thor84no Biasanya, saya akan berasumsi bahwa si penanya tidak benar-benar mencoba untuk mencocokkan dengan contoh yang dibuat-buat seperti kasus pertama, dan bahwa pertandingan di dunia nyata tidak begitu sederhana, dalam hal ini saya tidak berpikir RegEx akan berjalan menjadi cara yang tepat untuk melakukannya. Dengan kata lain, jika RegEx Anda hanyalah daftar opsi yang dipisahkan oleh karakter-pipa, itu tidak lebih mudah dibaca daripada saran lainnya, dan mungkin secara signifikan kurang efisien.
Moshe Katz
10

Jawaban luar biasa, tetapi Anda dapat membuat kode jauh lebih mudah dibaca dengan membungkus salah satu dari mereka dalam suatu fungsi.

Ini rumit jika pernyataan, ketika Anda (atau orang lain) membaca kode dalam waktu bertahun-tahun, Anda akan memindai untuk menemukan bagian untuk memahami apa yang terjadi. Pernyataan dengan tingkat logika bisnis ini akan menyebabkan Anda tersandung selama beberapa detik saat Anda mengerjakan apa yang Anda uji. Sedangkan kode seperti ini, akan memungkinkan Anda untuk melanjutkan pemindaian.

if(CheckIfBusinessRuleIsTrue())
{
    //Do Something
}

function CheckIfBusinessRuleIsTrue() 
{
    return (the best solution from previous posts here);
}

Beri nama fungsi Anda secara eksplisit sehingga segera jelas apa yang Anda uji dan kode Anda akan lebih mudah dipindai dan dipahami.

Fran Hoey
sumber
1
Jawaban terbaik yang saya lihat di sini. Sungguh, saya melihat bahwa orang tidak peduli dengan prinsip-prinsip desain yang baik. Hanya ingin memperbaiki sesuatu dengan cepat dan lupa memperbaiki kodenya sehingga sistem dapat dipertahankan di masa depan!
Maykonn
Bagaimana kalau berkomentar saja // CheckIfBusinessRuleIsTrue?
daniel1426
4

Anda bisa memasukkan semua jawaban ke dalam Javascript Set dan kemudian panggil saja .contains()set tersebut.

Anda masih harus mendeklarasikan semua konten, tetapi panggilan inline akan lebih pendek.

Sesuatu seperti:

var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}
Guido Anselmi
sumber
4
Itu tampak seperti cara yang sangat boros untuk mencapai apa yang OP coba lakukan. Jadi, sementara Anda bisa menyertakan perpustakaan pihak ketiga tambahan, instantiate objek, dan memanggil metode di atasnya, Anda mungkin tidak boleh.
KaptajnKold
@ Kapten Dingin: Yah OP meminta keringkasan bukan jejak memori. Mungkin set dapat digunakan kembali untuk operasi lain?
Guido Anselmi
1
Tentu, tetapi meskipun demikian: Apakah Anda dalam kejujuran pernah melakukan ini sendiri? Jika saya pernah melihat ini di alam liar, saya akan menganggapnya sebagai WTF utama.
KaptajnKold
1
Ya Anda benar (saya memberi Anda +1) tetapi ia menganggap bahwa pemeriksaan ini dilakukan di tempat lain. Jika itu dilakukan di beberapa tempat lain dan / atau tes berubah, maka penggunaan Set mungkin masuk akal. Saya serahkan pada OP untuk memilih solusi terbaik. Semua yang mengatakan jika ini adalah penggunaan soliter saya akan setuju bahwa menggunakan Set akan pantas sebagai topi As Clown of a malu.
Guido Anselmi
2
Saya benar-benar berpikir jawaban yang dipilih benar-benar mengerikan dan lebih buruk daripada menggunakan Set karena sama sekali tidak dapat dibaca.
Guido Anselmi
2

Salah satu cara favorit saya untuk menyelesaikan ini adalah dengan perpustakaan seperti underscore.js ...

var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
    return test.type === item;
});

if(isItem) {
    // One of them was true
}

http://underscorejs.org/#some

jcreamer898
sumber
1
containsbisa dibilang solusi yang lebih baik daripadasome
Dennis
1
Tidak perlu menggunakan perpustakaan untuk ini: someadalah fungsi pada prototipe Array di EC5.
KaptajnKold
2
Benar, tetapi tidak semua orang memiliki dukungan EC5. Plus, saya sangat suka garis bawah. :)
jcreamer898
Jika Anda sudah menggunakan perpustakaan seperti garis bawah, ini mungkin cara termudah. Jika tidak, masuk akal untuk memuat seluruh perpustakaan hanya untuk satu fungsi.
Moshe Katz
2

cara lain atau cara luar biasa lain yang saya temukan adalah ini ...

if ('a' in oc(['a','b','c'])) { //dosomething }

function oc(a)
{
  var o = {};
  for(var i=0;i<a.length;i++)  o[a[i]]='';
  return o;
}

tentu saja seperti yang Anda lihat ini membutuhkan beberapa langkah lebih maju dan membuatnya mudah mengikuti logika.

http://snook.ca/archives/javascript/testing_for_a_v

menggunakan operator seperti ~ && || ((), ()) ~~ tidak apa-apa hanya jika kode Anda rusak nanti. Anda tidak akan tahu harus mulai dari mana. Jadi keterbacaan itu BESAR.

jika harus, Anda bisa membuatnya lebih pendek.

('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);

dan jika Anda ingin melakukan invers

('a' in oc(['a','b','c'])) || statement;
Muhammad Umer
sumber
2

Cukup gunakan switchpernyataan alih-alih ifpernyataan:

switch (test.type) {

  case "itema":case "itemb":case "itemc":case "itemd":
    // do your process
  case "other cases":...:
    // do other processes
  default:
    // do processes when test.type does not meet your predictions.
}

Switch juga bekerja lebih cepat daripada membandingkan banyak persyaratan dalam sebuah if

unmultimedio
sumber
2

Untuk daftar string yang sangat panjang, ide ini akan menghemat beberapa karakter (tidak mengatakan saya akan merekomendasikannya dalam kehidupan nyata, tetapi seharusnya berhasil).

Pilih karakter yang Anda tahu tidak akan muncul di test.type Anda, gunakan sebagai pembatas, tempel semuanya menjadi satu string panjang dan cari yang:

if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
  // doSomething
}

Jika string Anda semakin dibatasi, Anda bahkan dapat menghilangkan pembatas ...

if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
  // doSomething
}

... tetapi Anda harus berhati-hati terhadap kesalahan positif dalam kasus itu (mis. "embite" akan cocok dengan versi itu)

CupawnTae
sumber
2

Agar mudah dibaca, buat fungsi untuk ujian (ya, fungsi satu baris):

function isTypeDefined(test) {
    return test.type == 'itema' ||
           test.type == 'itemb' ||
           test.type == 'itemc' ||
           test.type == 'itemd';
}

lalu sebut itu:


    if (isTypeDefined(test)) {

}
...
zaph
sumber
1

Saya pikir ada 2 tujuan ketika menulis kondisi if ini.

  1. keringkasan
  2. keterbacaan

Terkadang # 1 mungkin yang tercepat, tetapi saya akan mengambil # 2 untuk perawatan yang mudah nanti. Bergantung pada skenario, saya akan sering memilih variasi jawaban Walter.

Untuk memulai, saya memiliki fungsi yang tersedia secara global sebagai bagian dari perpustakaan saya yang ada.

function isDefined(obj){
  return (typeof(obj) != 'undefined');
}

dan kemudian ketika saya benar-benar ingin menjalankan jika kondisi mirip dengan Anda, saya akan membuat objek dengan daftar nilai yang valid:

var validOptions = {
  "itema":1,
  "itemb":1,
  "itemc":1,
  "itemd":1
};
if(isDefined(validOptions[test.type])){
  //do something...
}

Ini tidak secepat pernyataan switch / case dan sedikit lebih bertele-tele daripada beberapa contoh lainnya, tetapi saya sering mendapatkan kembali penggunaan objek di tempat lain dalam kode yang bisa sangat berguna.

Membonceng pada salah satu sampel jsperf yang dibuat di atas saya menambahkan tes ini dan variasi untuk membandingkan kecepatan. http://jsperf.com/if-statements-test-techsin/6 Hal paling menarik yang saya catat adalah bahwa kombo pengujian tertentu di Firefox jauh lebih cepat daripada Chrome.

scunliffe
sumber
1

Ini dapat diselesaikan dengan simpel untuk loop:

test = {};
test.type = 'itema';

for(var i=['itema','itemb','itemc']; i[0]==test.type && [
    (function() {
        // do something
        console.log('matched!');
    })()
]; i.shift());

Kami menggunakan bagian pertama loop for untuk menginisialisasi argumen yang ingin Anda cocokkan, bagian kedua untuk menghentikan loop for dari berjalan, dan bagian ketiga menyebabkan loop akhirnya keluar.


sumber