打破JavaScript中的嵌套循环的最佳方法是什么?


445

打破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

Answers:


1030

就像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不同,这些标签只能用于continuebreak,而Javascript没有goto


387
WTF为什么我在使用JavaScript的3年中没看到它使用过:/ ..
Salman von Abbas 2012年

39
MDN纯粹出于可读性考虑而说“避免使用标签”。为什么它不“可读”?当然,因为没有人使用它们。但是为什么他们不使用它们呢?...
XML

7
@Web_Designer我相信您的评论已过时。在MDN文档中,没有任何地方说“避免使用标签”。请考虑修改或删除您的评论。
肖恩·比恩

8
@SeantheBean完成。这看起来似乎是更直接的答案,并且不会受到滥用,因为它仅适用于continuebreak
加里·威洛比,2015年

29
@JérémyPouyet-您的不赞成投票的逻辑是愚蠢和毫无根据的。它完美地回答了OP的问题。问题与您对可读性的看法无关。请重新考虑您为社区提供帮助的方法。
Dembinski

168

将其包装在一个函数中,然后将其包装return


12
我选择接受此答案,因为它很简单并且可以优雅地实现。我绝对讨厌GOTO并认为它们是不好的做法(可以打开),Ephemient的做法太接近了。; o)
Gary Willoughby

16
IMO,GOTO可以的,只要它们不破坏结构即可。但是要每个人自己!
迅速

31
for循环上的标签除语法外,与GOTO 绝对没有任何共同点。它们只是打破外部循环的问题。中断最内层的循环没有任何问题,对吗?那么为什么您在打破外部循环方面有问题?
约翰·史密斯

11
请考虑接受其他答案。如果不对Andrew Hedges评论(感谢顺便说一句),我会想到:啊,所以javascript没有该功能。我敢打赌,社区中的许多人可能会忽略此评论,并认为相同。
约翰·史密斯

11
为什么Stack Overflow不具有让社区覆盖明显错误选择的答案的功能?:/
马特·哈金斯

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 2012年

20
@Evgeny:虽然某些JavaScript样式指南要求在大括号内使用大括号,但在换行中插入大括号并不是不正确的,并且解释程序不会有歧义地插入分号。ASI的行为已得到很好的定义,在这里不适用。
杰森·苏亚雷斯(JasonSuárez)2013年

9
只要确保用这种方法发表评论即可。现在还不是很清楚这里发生了什么。
Qix-蒙尼卡(Monica)2014年

1
好的简单答案。这应该被认为是答案,因为它不会占用大量CPU资源(这是使用函数时遇到的问题),也不会使用通常不可读或不应该使用的标签。:)
Girish Sortur 2015年

2
我可能会遗漏一些东西,但是要解决必须完成该迭代的内部循环问题,可以在设置z和y之后放入a break还是continue立即放入?我确实喜欢使用for循环条件进行踢球的想法。以自己的方式优雅。
Ben Sutton

76

我意识到这是一个非常老的话题,但是由于我的标准方法尚不完善,因此我认为我已将其发布给未来的Google员工。

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 2013年

7
不,它从两个循环中退出。这是示威的小提琴。无论您设置什么条件,它都将在满足后退出。
zord

4
优化将是增加休息时间;设置abort = true后;并从最终循环中删除!abort条件检查。
xer21

1
我喜欢这样,但是我认为从一般意义上来说,您将进行许多不必要的处理-也就是说,对于每个迭代器评估abort和表达式的每次迭代。在简单的场景中可能还不错,但是对于庞大的循环以及成百上千的迭代可能会是一个问题
Z. Khullah

1
你们真的在争论检查单个布尔值10000次是快还是慢?尝试1亿次/叹气
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
我想这就是
swiilliams

18
如果循环很大,这会增加可观的运行时成本-必须由Java解释器/编译器(或如今“比较器”,两者混合)为函数创建新的执行上下文(并在某些时候由GC释放)。每一次。
Mörre

2
这实际上非常危险,因为可能会发生一些您可能没有想到的奇怪的事情。特别地,由于使用var创建的闭包x,如果循环中的任何逻辑在以后的某个时间点引用x(例如,它定义了内部的匿名函数,该函数稍后保存并执行),则x的值将是它的任意值是在循环的末尾,而不是函数定义期间的索引。(续)
devios1 2012年

1
为了解决这个问题,您需要将x参数作为参数传递给匿名函数,以便它创建它的新副本,然后可以其引用为闭包,因为从那时起它不会改变。简而言之,我建议使用短暂的答案。
devios1 2012年

2
同样,我认为可读性是完全废话。这比标签更模糊。标签仅被视为不可读,因为没有人使用过它们。
Qix-蒙尼卡(Monica)2014年

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

我同意这实际上是最好的,函数无法扩展,如果循环也无法扩展,则将所有for循环包装起来,即难以阅读和调试...。这个功能很棒。您可以只声明vars loop1,loop2,loop3,并在末尾添加一点声明。另外,要打破多个循环,您需要执行类似操作loop1=loop2=false;
Muhammad Umer

22

以下是打破JavaScript中的嵌套循环的五种方法:

1)将父循环设置为结尾

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
@Wyck我不同意!令人遗憾的是,JavaScript并不break 2;像我们在PHP中那样具有简单的语法。没有循环标签,没有函数,没有if-else检查,没有通过/爆破循环变量进行调节-只是干净的语法!
杰·达达尼亚

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

对于递减类型的嵌套循环,有一个类似的答案,但这对递增类型的嵌套循环有效,而无需为简单循环考虑每个循环的终止值。

另一个例子:

// 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循环,而不是两个。
加里·威洛比

2

我以为我会展示一种功能编程方法。您可以像我的解决方案中那样,使用嵌套的Array.prototype.some()和/或Array.prototype.every()函数。这种方法的另一个好处是Object.keys()仅枚举对象自身的可枚举属性,而 “ for-in循环也枚举原型链中的属性”

接近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,我们可以使用some()来遍历数组,并在满足条件时停止,因此我的代码如下所示:

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
这看起来比原始版本更令人困惑。
克里斯蒂亚诺·丰特斯

21
就像一首后现代诗一样
-Digerkam,

之所以投票,是因为在大多数情况下,“做一会儿”变得越来越容易。
科迪2013年

-4

最好的方法是
-1)对在第一和第二循环中使用的两个数组进行排序。
2)如果项目匹配,则中断内部循环并保留索引值。
3)当开始下一次迭代时,使用保持索引值启动内部循环。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.