Koşullu ifadelerimi kısaltma


154

Aşağıdaki gibi çok uzun bir koşullu ifadem var:

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

Bu ifadeyi / ifadeyi daha özlü bir forma dönüştürüp aktaramayacağımı merak ediyordum.

Bunu nasıl başaracağınız hakkında bir fikriniz var mı?


23
Bunları bir diziye koyabilir ve in?
jeremy


şimdi sadece biri hangisinin en hızlı olduğunu kontrol edebilseydi
Muhammed Umer

3
Bu belki herkes için bir şok ama OP sahip olduğu hız net bir kazanan !!!!!!! Belki tarayıcı bunun için çok optimize neden .. Sonuçlar: (1) ile ||. (2) switchifadeler. (3) normal ifade. (4) ~. jsperf.com/if-statements-test-techsin
Muhammad Umer

3
Buna yanlış yaklaşıyor da olabilirsiniz. Bu durumda, bu 4 türün ortak bir yanı vardır. Bu ne? Bunu daha uç bir duruma götürürsek, bu duruma uyması için 10 tip daha eklememiz gerekiyorsa. Yoksa 100 mü? Daha fazlası olsaydı, muhtemelen bu çözümü ya da önerilen diğer çözümlerden birini kullanmayı düşünmezdiniz. Böyle büyük bir if ifadesi görüyorsunuz ve bunun bir kod kokusu olduğunu düşünüyorsunuz, bu iyi bir işaret. Bunu daha özlü hale getirmenin en iyi yolu if (test.your_common_condition) yazabilmenizdir. Bu bağlamda anlaşılması daha kolay ve daha genişletilebilir.
gmacdougall

Yanıtlar:


241

Değerlerinizi bir diziye yerleştirin ve öğenizin dizide olup olmadığını kontrol edin:

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

Desteklediğiniz bir tarayıcıda Array#includesyöntem yoksa, bu çoklu dolguyu kullanabilirsiniz .


~Yaklaşık işareti kısayolunun kısa açıklaması :

Güncelleme: Şimdi includesyönteme sahip olduğumuzdan, artık ~kesmek kullanmanın bir anlamı yok. Sadece burada nasıl çalıştığını ve / veya başkalarının kodunda karşılaştığını bilmek isteyen insanlar için burada tutmak.

Yerine sonucu, denetimi indexOfolduğunu >= 0, güzel küçük bir kısayol vardır:

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

İşte keman: http://jsfiddle.net/HYJvK/

Bu nasıl çalışıyor? Dizide bir öğe bulunursa indexOf, dizinini döndürür. Öğe bulunamadı ise, geri dönecektir -1. Çok fazla ayrıntıya girmeden, ~bir olduğunu bitsel NOT operatörü dönecektir, 0sadece -1.

~Kısayolu kullanmayı seviyorum , çünkü dönüş değeri üzerinde bir karşılaştırma yapmaktan daha özlü. Ben JavaScript in_arraydoğrudan (PHP benzer) bir Boolean döndüren bir işlevi olurdu isterdim , ama bu sadece arzulu düşünme ( Güncelleme: şimdi yapar. Denir includes. Yukarıya bakınız). Not jQuery en olduğunu inArray, PHP'nin yöntem imzası, aslında taklit yerli paylaşırken indexOf(dizin gerçekten sonra ne konum ise, farklı durumlarda yararlıdır) işlevselliği.

Önemli not: tilde kısayol kullanma bazıları, tartışmalara sarıldı gibi görünüyor şiddetle kod yeterince açık değildir ve her ne pahasına (Bu yanıt hakkındaki bakınız) kaçınılmalıdır inanıyoruz. Duygularını paylaşırsanız, .indexOf(...) >= 0çözüme bağlı kalmalısınız .


Biraz daha uzun açıklama:

JavaScript'teki tamsayılar imzalanır, yani en soldaki bit işaret biti olarak ayrılır; sayının pozitif mi negatif mi olduğunu gösteren bir bayrak 1.

32 bit ikili biçimde bazı örnek pozitif sayılar:

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

Şimdi burada aynı sayılar var, ancak negatif:

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

Negatif sayılar için neden bu kadar garip kombinasyonlar? Basit. Negatif bir sayı, pozitif sayının + 1'in tersidir; negatif sayıyı pozitif sayıya eklemek her zaman vermelidir 0.

Bunu anlamak için basit bir ikili aritmetik yapalım.

Burada eklersiniz nasıl -1için +1:

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

Ve burada eklersiniz nasıl -15için +15:

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

Bu sonuçları nasıl elde ederiz? Düzenli olarak ekleyerek, okulda bize öğretildiği şekilde: en sağdaki sütundan başlarsınız ve tüm satırları toplarsınız. Toplam, en büyük tek haneli sayıdan büyükse (ondalık sayı 9, ancak ikili sayıdır 1), kalan kısmı bir sonraki sütuna taşırız.

Şimdi, fark edeceğiniz gibi, pozitif sayısına negatif bir sayı eklerken, hepsi 0s olmayan en sağdaki sütunda her zaman iki 1s olacaktır 2; İki varlığın ikili gösterimi , sonraki sütuna 10taşıyoruz ve sonuç için ilk sütuna 1a koyuyoruz 0. Soldaki diğer tüm sütunlarda a ile yalnızca bir satır vardır 1, bu nedenle 1önceki sütundan taşınan tekrar toplanır 2, bu da devam eder ... Bu işlem, en soldaki sütuna ulaşana kadar kendini tekrarlar. 1taşınır Taşana ve kaybolur, böylece gidecek vardır ve biz kalacaksın edilecek 0genelinde s tüm.

Bu sisteme 2'nin Tamamlayıcısı denir . Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz:

2 İmzalı Tam Sayılar için Tamamlayıcı Temsil .


Şimdi, 2'nin tamamlayıcısındaki çökme kursu bittiğine göre, -1ikili temsili her yerde olan tek sayı olduğunu göreceksiniz 1.

~Bitsel NOT operatörünü kullanarak, belirli bir sayıdaki tüm bitler ters çevrilir. 0Tüm bitleri tersine çevirmekten geri dönmenin tek yolu, her şeyden başlayıp başlamadığımızdır 1.

Yani, bütün bu söyleyerek uzun soluklu bir yol olduğunu ~nancak dönecektir 0eğer nolduğunu -1.


59
Bitsel operatörleri kullanırken seksi olduğundan emin olmakla birlikte, !== -1akla gelebilecek herhangi bir yoldan gerçekten daha iyi mi? Açık boolean mantık, sıfıra sıfırlık biçimini dolaylı olarak kullanmaktan daha uygun değil mi?
Phil

21
Güzel teknik, ama hoşuma gitmedi. İlk bakışta kodun ne yaptığı net değil, bu da onu sürdürülemez kılıyor. "Yuriy Galanter" cevabını çok tercih ederim.
Jon Rea

65
-1 yeni programcılar Ben kodlarını korumak ve dışarı saçımı girse de 5 yıl sonra, kodlama bir serin ve kabul edilebilir bir yol olduğunu düşünüyorum, böyle yanıtları görmek
Danny Pflughoeft - BlueRaja

23
Bu deyim, uzmanlık alanlarım olan C #, Java veya Python gibi dillerde kesinlikle yaygın değildir. Ve burada birkaç yerel Javascript uzmanına sordum ve hiçbiri daha önce yapıldığını görmedi; bu nedenle iddia ettiğiniz kadar yaygın değildir. Her zaman daha açık ve daha yaygın lehine kaçınılmalıdır != -1.
BlueRaja - Danny Pflughoeft

12
-1, başlangıçta bit gösterimleri hakkında çok fazla şey belirtmeyen bir dilde gereksiz bitfiddling nedeniyle. Artı, bu cevabın büyük kısmı bitfiddling'i açıklıyor. Kesmeyi açıklamak için 20 paragraf yazmanız gerekiyorsa, GERÇEKTEN herhangi bir zamanda tasarruf sağlıyor mu?
kabarık

242

Switch deyimini fall thru ile kullanabilirsiniz:

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}

9
temelde if ile aynı, dizi yöntemi dizini çok daha iyi
NimChimpsky

6
@kojiro ne yazık ki, doğru cevap bu, ama harika bitwhise dizi hilesi yerine buna dikkat etmek imkansız.
Manu343726

1
Bu cevabın burada olması gerektiğini biliyordum ama bulmak için aşağıya inmek zorunda kaldım. Switch deyimi tam olarak bunun için tasarlandı ve diğer birçok dile taşıdı. Bir çok insanın bir açıklamadaki 'düşme' yöntemini bilmediğini gördüm.
Jimmy Johnson

3
Bu çözüm, Firefox ve Safari'de en hızlı ||ve Chrome'da en hızlı ikinci (orijinalinden sonra ). Bkz. Jsperf.com/if-statements-test-techsin
pabouk

3
Ben birçok koşul olsaydı bu bir yöntemle yazmak için korkunç olacağını düşünüyorum ... Eğer birkaç koşul için iyi, ama 10'dan fazla için ben kod temiz tutmak için dizi gitmek istiyorum.
yu_ominae

63

Bilimi kullanarak: idfah'ın söylediklerini yapmalısınız ve bu kodu en kısa hızda tutarken kodu kısa tutun:

BU HIZLI DAHA IS ~Yöntem

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

http://jsperf.com/if-statements-test-techsin resim açıklamasını buraya girin (Üst set: Krom, alt set: Firefox)

Sonuç:

Eğer olasılıklar vardır kaç ve bu belli olanları maksimum performans çıkmak daha oluşma olasılığı daha yüksektir biliyorum if ||, switch fall throughve if(obj[keyval]).

Eğer olasılıklar vardır birçok ve bunların herkes en başka bir deyişle, bir tane olarak meydana gelen, bir büyük olasılıkla nesne arama en iyi şekilde performans elde daha oluşmaya olanı bilemez olabilir if(obj[keyval])ve regexbu krizleri eğer.

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

yeni bir şey gelirse güncelleyeceğim.


2
Gerçekten iyi bir yazı için +1! Doğru anlarsam, switch caseen hızlı yöntem budur?
user1477388

1
firefox evet, krom onunif ( ...||...||...)...
Muhammad Umer

8
Bu giriş üzerinde gerçekten çok sayıda döngü yaparsanız daha hızlıdır, ancak çok büyük n ("itemX" dizelerinin sayısı) olan bir döngünüz varsa çok daha yavaştır. Doğrulamak (veya belki de çürütmek) için kullanabileceğiniz bu kod üreticisini hackledim . obj["itemX"]n büyükse son derece hızlıdır. Temel olarak, hızlı olan şey bağlama bağlıdır. İyi eğlenceler.
kojiro

3
Bu en hızlı yöntem, ama önemli mi?
congusbongus

1
@Mich Kodun zarafetini sadece hız için feda etmeyin. Birçok insan size söyleyecektir. Sonunda, sadece sağduyunuzu kullanın.
Andre Figueiredo

32

Dizelerle karşılaştırıyorsanız ve bir kalıp varsa, normal ifadeler kullanmayı düşünün.

Aksi takdirde, kısaltmaya çalışırken sadece kodunuzu gizleyeceğinden şüpheleniyorum. Güzelleştirmek için çizgileri sarmayı düşünün.

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

4
Bu cevap hızı açısından kazanır jsperf.com/if-statements-test-techsin
Muhammed Umer

1
Bu aynı zamanda proje bakım moduna girdiğinde (, gibi kurallarla (test.type == 'itemf' && foo.mode == 'detailed')) genişletmenin en kolay yolu
Izkata

16
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) {  }

Bir nesneyi ilişkilendirilebilir dizi olarak kullanmak oldukça yaygın bir şeydir, ancak JavaScript'in yerel bir kümesi olmadığından nesneleri ucuz kümeler olarak da kullanabilirsiniz.


FlyingCat'ın kısaltmaya çalıştığı if ifadesi normalden daha kısadır?
dcarson

1
ifTüm boşlukları kaldırırsanız, @dcarson OP'nin deyiminin koşulu 78 karakter alır. Eğer bu gibi yazmak eğer Mine 54 alır: test.type in {"itema":1,"itemb":1,"itemc":1,"itemd":1}. Temel olarak, her ek anahtar için her iki mayın kullanımı için dört karakter kullanır.
kojiro

1
ancak şunları yapabilirsiniz: if (olasılıklar [test.type]) ve 2 karakterden tasarruf edin! :)
dc5

15
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

veya öğeler o üniforma değilse, o zaman:

if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }

9
“Bazı insanlar bir sorunla karşılaştıklarında 'Biliyorum, düzenli ifadeler kullanacağım' diye düşünüyorlar. Şu an iki problemleri var." - Jamie Zawinski, 1997
Moshe Katz

5
@MosheKatz İnsanları bu alıntı hakkında küfretmeyi sevdiğimi anlayabiliyorum - ve insanlar kesinlikle uygun olmayan şeyler için düzenli ifadeler kullanıyorlar, ama bu onlardan biri değil. OP tarafından sağlanan durumda, bu sadece kriterlere uymakla kalmaz, çok iyi sonuç verir. Düzenli ifadeler doğal olarak kötü değildir ve dizeleri iyi tanımlanmış parametrelerle eşleştirmek bunun için yapılır.
Thor84no

3
@ Thor84no Genellikle, sorgulayıcının aslında ilk dava gibi tartışmalı bir örnekle eşleşmeye çalışmadığını ve gerçek dünya maçlarının o kadar basit olmadığını varsayarım, bu durumda bir RegEx'in gittiğini düşünmüyorum doğru yolu olmak. Başka bir deyişle, RegEx'iniz sadece karakterle ayrılmış bir seçenekler listesiyse, diğer önerilerin herhangi birinden daha okunabilir değildir ve muhtemelen daha az verimlidir.
Moshe Katz

10

Mükemmel yanıtlar, ancak bunlardan birini bir işlevde sararak kodu çok daha okunabilir hale getirebilirsiniz.

Bu, eğer (veya bir başkası) kodu yıllar içinde okuduğunuzda, neler olduğunu anlamak için bölümü bulmak için tararsanız, bu karmaşıktır. Bu düzeyde iş mantığına sahip bir ifade, test ettiğiniz şeyi çalışırken birkaç saniye tökezlemenize neden olur. Böyle bir kod olarak, taramaya devam etmenizi sağlar.

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

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

Neyi test ettiğinizin hemen anlaşılması için işlevinizi açıkça adlandırın ve kodunuzun taranması ve anlaşılması daha kolay olacaktır.


1
Burada gördüğüm en iyi cevap. Gerçekten, insanların iyi tasarım ilkelerini önemsemediğini görüyorum. Sadece bir şeyi hızlı bir şekilde düzeltmek ve sistemin ileride bakımı yapılabilmesi için kodu geliştirmeyi unutmayın!
Maykonn

Sadece yorum yapmaya ne dersiniz // CheckIfBusinessRuleIsTrue?
daniel1426

4

Tüm cevapları bir Javascript Setine koyabilir ve daha sonra .contains()seti arayabilirsiniz .

Yine de tüm içeriği beyan etmeniz gerekir, ancak hat içi çağrı daha kısa olacaktır.

Gibi bir şey:

var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}

4
Bu, OP'nin yapmaya çalıştığı şeyi başarmak için inanılmaz derecede savurgan bir yol gibi görünüyor. Eğer süre Yani olabilir ekstra bir 3. taraf kütüphane, bir nesnenin örneğini ve arama üzerinde bir yöntem, muhtemelen etmemelidir.
KaptajnKold

@Captain Cold: OP, bellek ayak izini değil özlü olmayı istedi. Belki de set diğer operasyonlar için tekrar kullanılabilir mi?
Guido Anselmi

1
Elbette, ama buna rağmen: dürüstçe misiniz siz hiç ödemeniz gerekmez? Bunu vahşi doğada görmüş olsaydım, büyük bir WTF olduğunu düşünürdüm.
KaptajnKold

1
Evet haklısın (sana + 1'leri verdim) ama bu kontrolün başka hiçbir yerde yapılmadığını varsayar. Başka yerlerde yapılırsa ve / veya test değişirse, Set'in kullanımı mantıklı olabilir. En iyi çözümü seçmek için OP'ye bırakıyorum. Bütün bunlar, bu yalnız bir kullanım olsaydı, Set'in kullanılmasının U Palyaço Olarak utanç şapkasını hak edeceğini kabul ediyorum.
Guido Anselmi

2
Ben aslında seçilen cevap kesinlikle korkunç ve Set okunamaz olduğu gibi kullanarak daha kötü olduğunu düşünüyorum.
Guido Anselmi

2

Bunu başarmanın en sevdiğim yollarından biri underscore.js gibi bir kütüphaneyle ...

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

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

http://underscorejs.org/#some


1
containstartışmasız daha iyi bir çözümsome
Dennis

1
Bunun için bir kitaplık kullanmaya gerek yok: someEC5'teki Array prototipinde bir işlevdir.
KaptajnKold

2
Doğru, ancak herkesin EC5 desteği yok. Ayrıca, alt çizgiyi gerçekten seviyorum. :)
jcreamer898

Eğer siz edilir zaten çizgi gibi bir kitaplığı kullanarak, bu muhtemelen en kolay yoludur. Aksi takdirde, sadece bir işlev için bir kitaplığın tamamını yüklemek çok az mantıklıdır.
Moshe Katz

2

başka bir yol ya da başka bir harika yolu buldum bu ...

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

Tabii ki bu bir şeyleri bir adım daha ileri götürür ve onları mantığı takip etmelerini kolaylaştırır.

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

~ && || gibi operatörleri kullanarak ((), ()) ~~ yalnızca kodunuz daha sonra bozulursa iyidir. Nereden başlayacağınızı bilemezsiniz. Dolayısıyla okunabilirlik BÜYÜK.

eğer gerekirse kısaltabilirsiniz.

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

ve ters yapmak istiyorsan

('a' in oc(['a','b','c'])) || statement;

2

Sadece switchifade yerine bir ifade kullanın if:

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 aynı zamanda bir koşuldaki birçok koşulu karşılaştırmaktan daha hızlı çalışır. if


2

Çok uzun dizeler listesi için, bu fikir birkaç karakter kazandıracaktır (gerçek hayatta tavsiye edeceğimi söylememekle birlikte, işe yaramalıdır).

Testinizde görünmeyeceğini bildiğiniz bir karakter seçin. Tür, ayırıcı olarak kullanın, hepsini tek bir uzun dizeye yapıştırın ve şunu arayın:

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

Dizeleriniz daha da kısıtlanırsa, sınırlayıcıları bile atlayabilirsiniz ...

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

... ancak bu durumda yanlış pozitiflere dikkat etmelisiniz (örn. "embite" bu sürümde eşleşir)


2

Okunabilirlik için test için bir işlev oluşturun (evet, bir satır işlevi):

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

sonra ara:


    if (isTypeDefined(test)) {

}
...

1

Bu tür if koşulunu yazarken 2 hedef olduğunu düşünüyorum.

  1. kısalık
  2. okunabilirliği

Bu nedenle bazen # 1 en hızlı olabilir, ancak daha sonra kolay bakım için # 2 alacağım. Senaryoya bağlı olarak Walter'ın cevabının bir varyasyonunu tercih ederim.

Başlamak için mevcut kütüphanemin bir parçası olarak dünya çapında kullanılabilir bir fonksiyonum var.

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

ve sizinkine benzer bir if koşulunu çalıştırmak istediğimde geçerli değerlerin bir listesini içeren bir nesne yaratırdım:

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

Bir anahtar / vaka ifadesi kadar hızlı değildir ve diğer örneklerden biraz daha ayrıntılıdır, ancak genellikle kodun başka bir yerinde nesnenin yeniden kullanılmasını sağlayabilirim.

Yukarıda yapılan jsperf örneklerinden birinde piggybacking Bu testi ve hızları karşılaştırmak için bir varyasyon ekledim. http://jsperf.com/if-statements-test-techsin/6 Belirttiğim en ilginç şey, Firefox'taki bazı test kombinasyonlarının Chrome'dan bile daha hızlı olmasıdır.


1

Bu, basit bir döngü ile çözülebilir:

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

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

Eşleştirmek istediğiniz bağımsız değişkenleri başlatmak için for döngüsünün ilk bölümünü, for döngüsünün çalışmasını durdurmak için ikinci bölümü ve döngünün sonunda çıkmasını sağlamak için üçüncü bölümü kullanırız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.