JavaScript'te iç içe döngülerden kurtulmanın en iyi yolu nedir?


448

Javascript'te iç içe döngülerden kurtulmanın en iyi yolu nedir?

//Write the links to the page.
for (var x = 0; x < Args.length; x++)
{
   for (var Heading in Navigation.Headings)
   {
      for (var Item in Navigation.Headings[Heading])
      {
         if (Args[x] == Navigation.Headings[Heading][Item].Name)
         {
            document.write("<a href=\"" 
               + Navigation.Headings[Heading][Item].URL + "\">" 
               + Navigation.Headings[Heading][Item].Name + "</a> : ");
            break; // <---HERE, I need to break out of two loops.
         }
      }
   }
}

Döngülerden ve kod bloklarından kurtulmanın iyi bir örneği: marcin-chwedczuk.github.io/…
csharpfolk

Yanıtlar:


1032

Tıpkı Perl gibi,

loop1:
    for (var i in set1) {
loop2:
        for (var j in set2) {
loop3:
            for (var k in set3) {
                break loop2;  // breaks out of loop3 and loop2
            }
        }
    }

EMCA-262 bölüm 12.12'de tanımlandığı gibi. [MDN Belgeleri]

C'den farklı olarak, bu etiketler yalnızca Javascript'te bulunmadığından continueve için kullanılabilir .breakgoto


387
WTF neden JavaScript ile 3 yıl içinde bir yerde kullanıldığını görmedim: / ..
Salman von Abbas

39
MDN, yalnızca okunabilirlik gerekçesiyle "etiket kullanmaktan kaçının" diyor. Neden 'okunabilir' değil? Çünkü elbette kimse onları kullanmıyor. Ama neden onları kullanmıyorlar? ...
XML

7
@Web_Designer Yorumunuzun güncel olmadığını düşünüyorum. MDN belgelerinde hiçbir yerde "etiket kullanmaktan kaçının" demez. Lütfen yorumunuzu gözden geçirmeyi veya kaldırmayı düşünün.
Fasulye Sean

8
@SeantheBean Bitti. Bu daha basit bir cevap gibi görünüyor ve kötüye kullanıma açık değil çünkü sadece continueve için kullanılabilir break.
Gary Willoughby

29
@ JérémyPouyet - Aşağı oylama mantığınız inane ve garanti dışı. OP'nin sorusuna mükemmel cevap veriyor. Soru, okunabilirlik konusundaki görüşlerinizle ilgili değildir. Lütfen topluluğa yardım etme yaklaşımınızı yeniden düşünün.
Dembinski

168

Bunu bir fonksiyona sarın ve sonra sadece return.


12
Bu cevabı kabul etmeyi seçiyorum çünkü basit ve zarif bir şekilde uygulanabilir. Kesinlikle GOTO'lardan nefret ediyorum ve onları kötü uygulama olarak görüyorum ( açılabilir ), Ephemient's çok yakın. ; o)
Gary Willoughby

16
IMO, GOTO'lar yapılandırmayı bozmadıkları sürece iyidir. Ama her biri kendi için!
ephemient

31
Döngüler için etiketlerin sözdizimleri dışında GOTO ile kesinlikle hiçbir ortak yanı yoktur . Bunlar dış döngülerden kopmak için meseledir. En içteki döngüyü kırmakla ilgili bir sorununuz yok, değil mi? Peki neden dış halkaları kırmayla ilgili bir sorunun var?
John Smith

11
Lütfen diğer cevabı kabul etmeyi düşünün. Andrew Hedges yorum için değilse (teşekkürler btw.), Düşünürdüm: ah, bu yüzden javascript bu özelliği yok. Ve bahse girerim, topluluktaki birçok kişi yorumu göz ardı edebilir ve aynı şekilde düşünebilir.
John Smith

11
Stack Overflow'ın neden topluluğun açıkça yanlış seçilmiş cevabı geçersiz kılmasına izin verme özelliği yok? : /
Matt Huggins

85

Partiye biraz geç kaldım, ancak aşağıdaki GOTO / etiket veya işlev kaydırma kullanmayan, dil bilincine sahip olmayan bir yaklaşımdır:

for (var x = Set1.length; x > 0; x--)
{
   for (var y = Set2.length; y > 0; y--)
   {
      for (var z = Set3.length; z > 0; z--)
      {
          z = y = -1; // terminates second loop
          // z = y = x = -1; // terminate first loop
      }
   }
}

Tersine, GOTO olmayan kalabalığı memnun etmek için doğal olarak akar. Aşağı yönde, iç döngü sonlandırmadan önce mevcut yinelemeyi tamamlaması gerekir, böylece bazı senaryolarda geçerli olmayabilir.


2
açılış ayracı yeni satırlarda olmamalıdır, çünkü js uygulamaları önceki satırın sonuna iki nokta üst üste yerleştirebilir.
Evgeny

21
@Evgeny: Bazı JavaScript stil rehberleri aynı hatta gitmek için kaşlı ayraçlar açmayı gerektirirken, yeni bir satıra sahip olmak yanlış değildir ve yorumlayıcının belirsiz bir şekilde noktalı virgül yerleştirme tehlikesi yoktur. ASI'nin davranışı iyi tanımlanmıştır ve burada geçerli değildir.
Jason Suárez

9
Sadece bu yaklaşımın dışına çıkmaya dikkat edin. Burada neler olduğu hemen belli değil.
Qix - MONICA

1
Güzel ve basit bir cevap. Bu, CPU yoğun döngülerini zorlamadığı için (işlevlerin kullanılmasıyla ilgili bir problemdir) veya genellikle okunamayan veya bazılarının söylediği gibi kullanılmaması gereken etiketleri kullanmadığı için cevap olarak düşünülmelidir. :)
Girish Sortur

2
Bir şey eksik olabilir, ancak yineleme bir koymak olabilir bitirmek zorunda iç döngü sorununu aşmanın breakveya continueset z ve y hemen sonra? Başlamak için fordöngünün koşullarını kullanma fikrini seviyorum . Kendi tarzında zarif.
Ben Sutton

76

Bunun gerçekten eski bir konu olduğunun farkındayım, ancak standart yaklaşımım henüz burada olmadığından, gelecekteki googlerslar için yayınladığımı düşündüm.

var a, b, abort = false;
for (a = 0; a < 10 && !abort; a++) {
    for (b = 0; b < 10 && !abort; b++) {
        if (condition) {
            doSomeThing();
            abort = true;
        }
    }
}

2
Eğer conditioniçin değerlendirir trueiç içe döngünün ilk yinelemesi üzerine, yine kontrol 10 tekrarlamalar geri kalanında çalıştırmak abortdeğeri her seferinde. Bu, 10 yineleme için bir performans sorunu değildir, ancak 10.000 ile olacaktır.
Robusto

7
Hayır, her iki döngüden de çıkıyor. İşte gösteren keman . Hangi koşulu ayarlarsanız yapın, sağlandıktan sonra çıkıyor.
zord

4
Optimizasyon bir mola eklemek olacaktır; iptal ettikten sonra = true; ve son döngüden!
xer21

1
Bunu beğendim ama genel anlamda birçok gereksiz işlem yapacağınızı düşünüyorum - yani her yineleyici değerlendirmesinin abortve ifadesinin her bir yinelemesi için . Basit senaryolarda bu iyi olabilir, ancak gazillion yinelemeleri olan büyük döngüler için bu bir sorun olabilir
Z. Khullah

1
Tek bir boole değerini 10000 kez kontrol etmenin hızlı mı yoksa yavaş mı olduğunu gerçekten mi tartışıyorsunuz? 100 milyon kez deneyin / nefes verin
fabspro

40
var str = "";
for (var x = 0; x < 3; x++) {
    (function() {  // here's an anonymous function
        for (var y = 0; y < 3; y++) {
            for (var z = 0; z < 3; z++) {
                // you have access to 'x' because of closures
                str += "x=" + x + "  y=" + y + "  z=" + z + "<br />";
                if (x == z && z == 2) {
                    return;
                }
            }
        }
    })();  // here, you execute your anonymous function
}

O nasıl? :)


2
Ben swilliams alıyorum ne olduğunu düşündüm
harley.333

18
Bu, döngü büyükse önemli çalışma zamanı maliyeti ekler - işlev için yeni bir yürütme bağlamı (ve GC tarafından bir noktada serbest bırakılmış) Javascript yorumlayıcısı / derleyicisi tarafından (veya bu günlerde "karşılaştırıcı") oluşturulmalıdır. HER SEFERİNDE.
Mörre

2
Bu aslında oldukça tehlikelidir, çünkü beklemeyebileceğiniz bazı garip şeyler olabilir. Özellikle, var ile oluşturulan kapatma nedeniyle x, döngü içindeki herhangi bir mantık daha sonraki bir zamanda x'e başvuruyorsa (örneğin, daha sonra kaydedilen ve yürütülen iç anonim bir işlevi tanımlar), x için değer ne olursa olsun oldu sonunda döngü, işlev esnasında tanımlandı değil indeksi. (devamı)
devios1

1
Bunun üstesinden gelmek xiçin, anonim işlevinize bir parametre olarak iletmeniz gerekir, böylece yeni bir kopyasını oluşturur; bu, daha sonra bu noktadan itibaren değişmeyeceğinden, bir kapatma olarak başvurulabilir. Kısacası, ephemient'in cevabını öneriyorum.
devios1

2
Ayrıca, okunabilirlik olayının tam bir saçmalık olduğunu düşünüyorum. Bu bir etiketten çok daha belirsiz. Etiketler yalnızca okunamaz olarak görülüyor çünkü hiç kimse bunları kullanmıyor.
Qix - MONICA

39

Oldukça basit:

var a = [1, 2, 3];
var b = [4, 5, 6];
var breakCheck1 = false;

for (var i in a) {
    for (var j in b) {
        breakCheck1 = true;
        break;
    }
    if (breakCheck1) break;
}

Bu aslında en iyi, aynı zamanda ölçek yani okuma ve hata ayıklama için zor yapar eğer döngüler için tüm kaydırma fonksiyonu, ölçek değil .... katılıyorum .... Bu harika. Sadece vars loop1, loop2, loop3 bildirebilir ve sonunda küçük bir ifade ekleyebilirsiniz. Ayrıca birden fazla döngüyü kırmak için şöyle bir şey yapmanız gerekirloop1=loop2=false;
Muhammad Umer

22

JavaScript'te iç içe döngülerden kurtulmanın beş yolu:

1) Üst (ler) döngüsünü sonuna kadar ayarla

for (i = 0; i < 5; i++)
{
    for (j = 0; j < 5; j++)
    {
        if (j === 2)
        {
            i = 5;
            break;
        }
    }
}

2) etiketi kullanın

exit_loops:
for (i = 0; i < 5; i++)
{
    for (j = 0; j < 5; j++)
    {
        if (j === 2)
            break exit_loops;
    }
}

3) Değişken kullanın

var exit_loops = false;
for (i = 0; i < 5; i++)
{
    for (j = 0; j < 5; j++)
    {
        if (j === 2)
        {
            exit_loops = true;
            break;
        }
    }
    if (exit_loops)
        break;
}

4) Kendi kendini yürütme işlevini kullanın

(function()
{
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
             if (j === 2)
                 return;
        }
    }
})();

5) düzenli fonksiyonu Kullanın

function nested_loops()
{
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
             if (j === 2)
                 return;
        }
    }
}
nested_loops();

1
@Wyck Yeterince katılıyorum! Bu break 2;bizim PHP gibi bir sözdizimi sadece bir utanç javascript değildir . Döngü etiketi yok, işlev yok, eğer başka kontrol yok, döngü değişkenleri ile temperleme yok - sadece sözdizimini temizleyin!
Jay Dadhania

1
Örnek 4 şık
leroyjenkinss24

14

Hiç kesinti yok, iptal bayrağı yok ve ekstra durum kontrolü yok. Bu sürüm Number.MAX_VALUE, koşul karşılandığında döngü değişkenlerini patlatır (bunları yapar ) ve tüm döngüleri zarif bir şekilde sonlandırmaya zorlar.

// No breaks needed
for (var i = 0; i < 10; i++) {
  for (var j = 0; j < 10; j++) {
    if (condition) {
      console.log("condition met");
      i = j = Number.MAX_VALUE; // Blast the loop variables
    }
  }
}

Azalan tip iç içe döngüler için benzer bir cevap vardı, ancak bu, her ilmeğin basit döngüler için sonlandırma değerini göz önünde bulundurmaya gerek kalmadan tip iç içe döngülerin artırılması için çalışır.

Başka bir örnek:

// No breaks needed
for (var i = 0; i < 89; i++) {
  for (var j = 0; j < 1002; j++) {
    for (var k = 0; k < 16; k++) {
      for (var l = 0; l < 2382; l++) {
        if (condition) {
          console.log("condition met");
          i = j = k = l = Number.MAX_VALUE; // Blast the loop variables
        }
      }
    }
  }
}

4

Döngüleri uç sınırlarına itmeye ne dersiniz?

    for(var a=0; a<data_a.length; a++){
       for(var b=0; b<data_b.length; b++){
           for(var c=0; c<data_c.length; c++){
              for(var d=0; d<data_d.length; d++){
                 a =  data_a.length;
                 b =  data_b.length;
                 c =  data_b.length;
                 d =  data_d.length;
            }
         }
       }
     }

1
Bence Drakes cevabı aynı mantığa daha özlü ve açık bir şekilde sahip.
Mühendis Tost

kesinlikle mükemmel!
geoyws

3

Coffeescript kullanıyorsanız, anonim bir işlevi tanımlamayı ve hemen yürütmeyi kolaylaştıran kullanışlı bir "do" anahtar kelimesi vardır:

do ->
  for a in first_loop
    for b in second_loop
      if condition(...)
        return

... böylece döngülerden çıkmak için "return" komutunu kullanabilirsiniz.


Bu aynı değil. Orijinal forörneğimde iki değil üç döngü var.
Gary Willoughby

2

İşlevsel bir programlama yaklaşımı göstereceğimi düşündüm. Çözümlerimdeki gibi iç içe Array.prototype.some () ve / veya Array.prototype.every () işlevlerinden çıkabilirsiniz. Bu yaklaşımın ek bir yararı, Object.keys()yalnızca bir nesnenin kendi numaralandırılabilir özelliklerini numaralandırmasıdır, oysa "bir for-in döngüsü prototip zincirindeki özellikleri de numaralandırır" .

OP'nin çözümüne yakın:

    Args.forEach(function (arg) {
        // This guard is not necessary,
        // since writing an empty string to document would not change it.
        if (!getAnchorTag(arg))
            return;

        document.write(getAnchorTag(arg));
    });

    function getAnchorTag (name) {
        var res = '';

        Object.keys(Navigation.Headings).some(function (Heading) {
            return Object.keys(Navigation.Headings[Heading]).some(function (Item) {
                if (name == Navigation.Headings[Heading][Item].Name) {
                    res = ("<a href=\""
                                 + Navigation.Headings[Heading][Item].URL + "\">"
                                 + Navigation.Headings[Heading][Item].Name + "</a> : ");
                    return true;
                }
            });
        });

        return res;
    }

Başlıklar / Öğeler üzerindeki yinelemeyi azaltan çözüm:

    var remainingArgs = Args.slice(0);

    Object.keys(Navigation.Headings).some(function (Heading) {
        return Object.keys(Navigation.Headings[Heading]).some(function (Item) {
            var i = remainingArgs.indexOf(Navigation.Headings[Heading][Item].Name);

            if (i === -1)
                return;

            document.write("<a href=\""
                                         + Navigation.Headings[Heading][Item].URL + "\">"
                                         + Navigation.Headings[Heading][Item].Name + "</a> : ");
            remainingArgs.splice(i, 1);

            if (remainingArgs.length === 0)
                return true;
            }
        });
    });

2

Daha önce swilliams tarafından daha önce bahsedilmiş , ancak aşağıdaki bir örnekle (Javascript):

// Function wrapping inner for loop
function CriteriaMatch(record, criteria) {
  for (var k in criteria) {
    if (!(k in record))
      return false;

    if (record[k] != criteria[k])
      return false;
  }

  return true;
}

// Outer for loop implementing continue if inner for loop returns false
var result = [];

for (var i = 0; i < _table.length; i++) {
  var r = _table[i];

  if (!CriteriaMatch(r[i], criteria))
    continue;

  result.add(r);
}

0

Hmmm 10 yaşındaki partiye merhaba?

Neden senin için bir şart koymuyorsun?

var condition = true
for (var i = 0 ; i < Args.length && condition ; i++) {
    for (var j = 0 ; j < Args[i].length && condition ; j++) {
        if (Args[i].obj[j] == "[condition]") {
            condition = false
        }
    }
}

Bunun gibi istediğin zaman durursun

Benim durumumda, Typescript kullanarak, dizi geçip koşul karşılandığında durduğumuz bazı () kullanabilirsiniz Kodum şöyle olur:

Args.some((listObj) => {
    return listObj.some((obj) => {
        return !(obj == "[condition]")
    })
})

Bu şekilde, koşul sağlandıktan hemen sonra döngü durdu

Hatırlatma: Bu kod TypeScript'te çalışır


-3
XXX.Validation = function() {
    var ok = false;
loop:
    do {
        for (...) {
            while (...) {
                if (...) {
                    break loop; // Exist the outermost do-while loop
                }
                if (...) {
                    continue; // skips current iteration in the while loop
                }
            }
        }
        if (...) {
            break loop;
        }
        if (...) {
            break loop;
        }
        if (...) {
            break loop;
        }
        if (...) {
            break loop;
        }
        ok = true;
        break;
    } while(true);
    CleanupAndCallbackBeforeReturning(ok);
    return ok;
};

9
Bu orijinalden daha kafa karıştırıcı görünüyor.
Cristiano Fontes

21
Postmodern bir şiir gibi
Digerkam

Bir do while daha fazla bu tür bir senaryoya (çoğu durumda) dönüştüğü için oylandı.
Cody

-4

en iyi yol -
1) Birinci ve ikinci döngüde kullanılan her iki diziyi sıralayın.
2) öğe eşleşirse, iç döngüyü kırın ve dizin değerini tutun.
3) bir sonraki yinelemeye başladığında tutma indeksi değeri ile iç döngüyü başlatın.

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.