อะไรคือวิธีที่ดีที่สุดในการแยกจากลูปซ้อนใน JavaScript


448

เป็นวิธีที่ดีที่สุดในการแยกจากลูปซ้อนใน Javascript อะไร

//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.
         }
      }
   }
}

นี่เป็นตัวอย่างที่ดีของการแยกลูปและบล็อกออกเป็นรหัส: marcin-chwedczuk.github.io/ …
csharpfolk

คำตอบ:


1032

เหมือน Perl

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 มาตรา 12.12 [เอกสาร MDN]

ซึ่งแตกต่างจาก C, ป้ายชื่อเหล่านี้เท่านั้นที่สามารถใช้สำหรับการcontinueและbreakเป็น Javascript gotoไม่ได้


387
WTF เหตุใดฉันจึงไม่เห็นสิ่งนี้ถูกใช้ใน 3 ปีของฉันด้วย JavaScript: / ..
Salman von Abbas

39
MDN กล่าวว่า "หลีกเลี่ยงการใช้ป้ายกำกับ" โดยละเอียด ทำไมมันถึงไม่ 'อ่านได้'? เพราะไม่มีใครใช้มันแน่นอน แต่ทำไมพวกเขาถึงไม่ใช้ ...
XML

7
@ Web_Designer ฉันเชื่อว่าความคิดเห็นของคุณล้าสมัย ไม่มีที่ไหนในเอกสาร MDN ที่ว่า "หลีกเลี่ยงการใช้ป้ายกำกับ" โปรดพิจารณาแก้ไขหรือลบความคิดเห็นของคุณ
Sean the Bean

8
@SantantheBean เรียบร้อยแล้ว นี้จะดูเหมือนเป็นคำตอบที่ตรงไปตรงมามากขึ้นและไม่เปิดให้ละเมิดเพราะมันใช้ได้เฉพาะกับและcontinue break
Gary Willoughby

29
@ JérémyPouyet - ตรรกะของคุณสำหรับการลงคะแนนเสียงไม่ได้รับอนุญาตและไม่มีเหตุผล มันตอบคำถามของ OP ได้อย่างสมบูรณ์แบบ คำถามไม่เกี่ยวข้องกับความคิดเห็นของคุณเกี่ยวกับความชัดเจน โปรดทบทวนวิธีการของคุณในการช่วยเหลือชุมชน
Dembinski

168

returnห่อขึ้นในการทำงานแล้วก็


12
ฉันเลือกที่จะยอมรับคำตอบนี้เพราะมันเรียบง่ายและสามารถนำไปใช้ในรูปแบบที่สง่างาม ฉันเกลียด GOTO จริง ๆ และพิจารณาว่าพวกเขาปฏิบัติไม่ดี ( เปิดได้ ) Ephemient ใกล้เกินไปแล้ว ; o)
Gary Willoughby

16
IMO, GOTO นั้นใช้ได้ตราบใดที่พวกเขาไม่ทำลายโครงสร้าง แต่สำหรับตัวเอง!
ephemient

31
ป้ายกำกับสำหรับลูปไม่มีอะไรเหมือนกันกับ GOTO ยกเว้นไวยากรณ์ พวกมันเป็นเพียงเรื่องที่ต้องแยกจากลูปนอก คุณไม่มีปัญหาอะไรกับการทำลายวงในสุดใช่ไหม? แล้วทำไมคุณถึงมีปัญหากับการทำลายลูปนอก?
John Smith

11
โปรดพิจารณายอมรับคำตอบอื่น ๆ หากไม่ใช่เพราะความคิดเห็นของ Andrew Hedges (ขอบคุณ btw.) ฉันคิดว่า: ah ดังนั้น javascript ไม่มีคุณสมบัติดังกล่าว และฉันพนันว่าหลายคนในชุมชนอาจมองข้ามความคิดเห็นและคิดเหมือนกัน
John Smith

11
เหตุใด Stack Overflow จึงไม่มีฟีเจอร์เพื่อให้ชุมชนแทนที่คำตอบที่เลือกผิดอย่างเห็นได้ชัด : /
Matt Huggins

85

ฉันมาช้าไปงานปาร์ตี้ แต่ต่อไปนี้เป็นวิธีใช้ภาษาที่ไม่เชื่อเรื่องพระเจ้าซึ่งไม่ได้ใช้ GOTO / ฉลากหรือการห่อฟังก์ชั่น:

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

คว่ำมันไหลอย่างเป็นธรรมชาติซึ่งควรทำให้ฝูงชนที่ไม่ใช่ GOTO พอใจ ข้อเสียวงด้านในจำเป็นต้องทำการวนซ้ำปัจจุบันให้เสร็จสิ้นก่อนที่จะสิ้นสุดดังนั้นจึงอาจไม่สามารถใช้งานได้ในบางสถานการณ์


2
วงเล็บปีกกาเปิดไม่ควรอยู่ในบรรทัดใหม่เพราะการใช้งาน js อาจแทรกลำไส้ใหญ่ในตอนท้ายของบรรทัดก่อนหน้า
Evgeny

21
@Evgeny: ในขณะที่ไกด์สไตล์ JavaScript บางคนเรียกร้องให้มีการเปิดวงเล็บปีกกาในบรรทัดเดียวกันมันไม่ถูกต้องที่จะใส่มันในบรรทัดใหม่และไม่มีอันตรายจากล่ามที่ใส่เครื่องหมายอัฒภาคอย่างไม่ชัดเจน พฤติกรรมของ ASI นั้นถูกกำหนดไว้อย่างดีและไม่สามารถใช้งานได้ที่นี่
Jason Suárez

9
เพียงให้แน่ใจว่าได้แสดงความคิดเห็นนรกจากวิธีการนี้ ไม่ชัดเจนว่าเกิดอะไรขึ้นที่นี่ในทันที
Qix - MONICA ถูกยกเลิก

1
คำตอบที่ดีและเรียบง่าย นี่ถือเป็นคำตอบเนื่องจากไม่ได้ทำให้เกิดการวนซ้ำของ CPU มาก (ซึ่งเป็นปัญหากับการใช้งานฟังก์ชั่น) หรือไม่ใช้ฉลากซึ่งโดยปกติจะไม่สามารถอ่านได้หรือไม่ควรใช้ตามที่บางคนกล่าว :)
Girish Sortur

2
ฉันอาจจะหายไปบางอย่าง แต่เพื่อแก้ไขปัญหาของลูปด้านในที่จะต้องทำซ้ำนั้นคุณสามารถใส่ a breakหรือcontinueทันทีหลังจากที่คุณตั้งค่า z และ y? ฉันชอบความคิดในการใช้forเงื่อนไขของวงเพื่อเริ่มต้น สง่างามในแบบของตัวเอง
Ben Sutton

76

ฉันรู้ว่านี่เป็นหัวข้อที่เก่ามาก แต่เนื่องจากวิธีการมาตรฐานของฉันไม่ได้อยู่ที่นี่ฉันจึงคิดว่าฉันจะโพสต์ไว้สำหรับนักเขียนภาพในอนาคต

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
หากconditionประเมินไปtrueที่การวนซ้ำครั้งแรกของลูปที่ซ้อนกันคุณยังคงวิ่งผ่านการวนซ้ำที่เหลืออีก 10 ครั้งโดยตรวจสอบabortค่าในแต่ละครั้ง นี่ไม่ใช่ปัญหาด้านประสิทธิภาพสำหรับการทำซ้ำ 10 ครั้ง แต่จะต้องบอก 10,000 ข้อ
Robusto

7
ไม่มันออกจากลูปทั้งสองแล้ว นี่คือแสดงให้เห็นถึงซอ ไม่ว่าคุณจะตั้งเงื่อนไขแบบไหนมันก็จะออกมาหลังจากที่เจอแล้ว
Zord

4
การเพิ่มประสิทธิภาพจะเป็นการเพิ่มตัวแบ่ง หลังจากตั้งค่าการยกเลิก = true; และลบ! ยกเลิกการตรวจสอบสภาพจากลูปสุดท้าย
xer21

1
ฉันชอบสิ่งนี้ แต่ฉันคิดว่าในแง่ทั่วไปคุณจะต้องทำการประมวลผลที่ไม่จำเป็นมากมายนั่นคือสำหรับการวนซ้ำของการวนซ้ำทุกครั้งจะประเมินabortและการแสดงออก ในสถานการณ์ง่าย ๆ ที่อาจจะใช้ได้ แต่สำหรับลูปขนาดใหญ่ที่มีการวนซ้ำ
หลายพัน

1
พวกคุณโต้เถียงกันหรือไม่ว่าการตรวจสอบค่าบูลีนเดี่ยว 10,000 ครั้งเร็วหรือช้า? ลอง 100 ล้านครั้ง / ถอนหายใจ
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
}

วิธีที่ว่า? :)


2
ฉันคิดว่านี่คือสิ่งที่
swilliams

18
สิ่งนี้จะเพิ่มต้นทุนรันไทม์ที่สำคัญหากลูปมีขนาดใหญ่ - บริบทการดำเนินการใหม่สำหรับฟังก์ชั่นจะต้องสร้างขึ้น (และในบางจุดที่เป็นอิสระจาก GC) โดยล่าม / คอมไพเลอร์ Javascript (หรือ "คอมไพเลอร์" ในวันนี้ ทุกเวลา
Mörre

2
จริง ๆ แล้วค่อนข้างอันตรายเนื่องจากสิ่งแปลก ๆ บางอย่างสามารถเกิดขึ้นได้ที่คุณอาจไม่คาดหวัง โดยเฉพาะอย่างยิ่งเนื่องจากการปิดที่สร้างขึ้นด้วย var xหากตรรกะใด ๆ ภายในลูปอ้างอิง x ในเวลาต่อมา (ตัวอย่างเช่นมันกำหนดฟังก์ชั่นที่ไม่ระบุชื่อภายในที่ถูกบันทึกและดำเนินการในภายหลัง) ค่าสำหรับ x จะเป็นอะไรก็ตาม อยู่ที่ส่วนท้ายของลูปไม่ใช่ดัชนีที่ฟังก์ชันกำหนดไว้ในระหว่างนั้น (ต่อ)
devios1

1
จะได้รับรอบนี้คุณต้องผ่านการxเป็นพารามิเตอร์เพื่อฟังก์ชั่นที่ไม่ระบุชื่อของคุณเพื่อที่จะสร้างสำเนาใหม่ของมันซึ่งสามารถแล้วถูกอ้างอิงเป็นปิดเพราะมันจะไม่เปลี่ยนแปลงจากจุดนั้น ในระยะสั้นฉันแนะนำคำตอบของ ephemient
devios1

2
เช่นกันฉันคิดว่าสิ่งที่อ่านง่ายคืออึสมบูรณ์ นี่เป็นวิธีที่คลุมเครือมากกว่าป้ายกำกับ ป้ายกำกับจะเห็นว่าอ่านไม่ได้เพราะไม่มีใครเคยใช้
Qix - MONICA ถูกยกเลิก

39

ค่อนข้างง่าย:

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

ฉันยอมรับว่านี่เป็นจริงที่ดีที่สุดฟังก์ชั่นหนึ่งไม่ได้ปรับขนาดทั้งหมดเพื่อการวนซ้ำหากยังไม่ได้ปรับขนาดเช่นทำให้อ่านยากและตรวจแก้จุดบกพร่อง .... อันนี้ยอดเยี่ยม คุณสามารถประกาศ vars loop1, loop2, loop3 และเพิ่มคำสั่งเล็กน้อยที่ส่วนท้าย คุณต้องทำบางอย่างเช่นloop1=loop2=false;
Muhammad Umer

22

นี่คือห้าวิธีในการแบ่งลูปซ้อนใน JavaScript:

1) ตั้งค่า parent parent ไปที่ท้ายสุด

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

2) ใช้ป้ายกำกับ

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

3) ใช้ตัวแปร

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) ใช้ฟังก์ชั่นการดำเนินการด้วยตนเอง

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

5) ใช้ฟังก์ชั่นปกติ

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

1
@ ไวค์ฉันเห็นด้วยไม่พอ! มันเป็นความอัปยศ javascript ไม่เพียง แต่มีไวยากรณ์break 2;เหมือนที่เรามีใน PHP ไม่มีเลเบลวนวน, ไม่มีฟังก์ชั่น, ไม่มีการตรวจสอบถ้าไม่มี, การแบ่งเบาบรรเทาด้วย / การระเบิดตัวแปรลูป - เพียงล้างไวยากรณ์!
Jay Dadhania

1
ตัวอย่าง 4 คือดี
leroyjenkinss24

14

วิธีการเกี่ยวกับการใช้ตัวแบ่งไม่เลยไม่มีการยกเลิกสถานะและไม่มีการตรวจสอบเงื่อนไขเพิ่มเติม รุ่นนี้จะระเบิดตัวแปรลูป (ทำให้พวกเขาNumber.MAX_VALUE) เมื่อตรงตามเงื่อนไขและบังคับให้ลูปทั้งหมดยุติอย่างสวยงาม

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

มีคำตอบที่คล้ายกันคือ ish สำหรับการวนซ้ำซ้อนกันแบบลดลง แต่ใช้งานได้กับการวนซ้ำแบบซ้อนเพิ่มชนิดโดยไม่จำเป็นต้องพิจารณาค่าการเลิกจ้างของแต่ละลูปสำหรับลูปแบบง่าย

ตัวอย่างอื่น:

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

วิธีการผลักลูปไปจนถึงขีด จำกัด

    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
ฉันคิดว่าคำตอบของ Drakesมีตรรกะเดียวกันในลักษณะที่กระชับและชัดเจนยิ่งขึ้น
Engineer Toast

ยอดเยี่ยมอย่างแน่นอน!
geoyws

3

หากคุณใช้ Coffeescript มีคีย์เวิร์ด "do" ที่สะดวกซึ่งทำให้ง่ายต่อการกำหนดและเรียกใช้ฟังก์ชันที่ไม่ระบุชื่อทันที:

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

... เพื่อให้คุณสามารถใช้ "ส่งคืน" เพื่อออกจากลูปได้


มันไม่เหมือนกัน ตัวอย่างดั้งเดิมของฉันมีสามforลูปไม่ใช่สอง
Gary Willoughby

2

ฉันคิดว่าฉันจะแสดงวิธีการเขียนโปรแกรมฟังก์ชั่น คุณสามารถแยกฟังก์ชั่น Array.prototype.some () และ / หรือ Array.prototype.every () ที่ซ้อนกันเช่นเดียวกับในโซลูชันของฉัน สิทธิประโยชน์เพิ่มเติมของวิธีนี้คือการที่Object.keys()ระบุเพียงวัตถุนับคุณสมบัติของตัวเองในขณะที่ "สำหรับในระบุห่วงคุณสมบัติในห่วงโซ่ต้นแบบได้เป็นอย่างดี"

ใกล้กับโซลูชันของ OP:

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

โซลูชันที่ลดการวนซ้ำในส่วนหัว / รายการ:

    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

ได้กล่าวถึงแล้วโดยswilliamsแต่มีตัวอย่างด้านล่าง (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

อืมสวัสดีกับปาร์ตี้ 10 ปีเหรอ?

ทำไมไม่ใส่เงื่อนไขของคุณใน?

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

เช่นนี้คุณหยุดเมื่อคุณต้องการ

ในกรณีของฉันใช้ typescript เราสามารถใช้บางอย่าง () ซึ่งผ่านอาร์เรย์และหยุดเมื่อพบเงื่อนไขดังนั้นรหัสของฉันเป็นเช่นนี้

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

เช่นนี้ลูปจะหยุดทันทีหลังจากตรงตามเงื่อนไข

คำเตือน: รหัสนี้ทำงานใน TypeScript


-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
นี่ดูสับสนกว่าต้นฉบับ
Cristiano Fontes

21
เหมือนบทกวีหลังสมัยใหม่
Digerkam

ให้คะแนนเนื่องจากสิ่งที่ต้องทำขณะอยู่ในสถานการณ์ประเภทนี้มากขึ้น (ในกรณีส่วนใหญ่)
โคดี้

-4

วิธีที่ดีที่สุดคือ -
1) เรียงลำดับอาร์เรย์ทั้งสองที่ใช้ในการวนรอบแรกและครั้งที่สอง
2) ถ้าจับคู่รายการแล้วแบ่งลูปด้านในและถือค่าดัชนี
3) เมื่อเริ่มการวนซ้ำครั้งถัดไปให้เริ่มวงภายในด้วยค่าดัชนีพัก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.