打破 JavaScript 中嵌套循环的最佳方法是啥?
Posted
技术标签:
【中文标题】打破 JavaScript 中嵌套循环的最佳方法是啥?【英文标题】:What's the best way to break from nested loops in JavaScript?打破 JavaScript 中嵌套循环的最佳方法是什么? 【发布时间】:2010-09-16 00:55:29 【问题描述】:在 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/… 【参考方案1】:分配比较条件中的值
function test()
for(var i=0;i<10;i++)
for(var j=0;j<10;j++)
if(somecondition)
//code to Break out of both loops here
i=10;
j=10;
//Continue from here
【讨论】:
【参考方案2】:上面有很多优秀的解决方案。 IMO,如果您的休息条件是例外, 你可以使用try-catch:
try
for (var i in set1)
for (var j in set2)
for (var k in set3)
throw error;
catch (error)
【讨论】:
【参考方案3】:就像 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 Docs]
与 C 不同,这些标签只能用于 continue
和 break
,因为 Javascript 没有 goto
。
【讨论】:
WTF 为什么我在 3 年的 JavaScript 中没有看到它被用于某个地方:/.. MDN 说“避免使用标签”纯粹是出于可读性的原因。为什么它不“可读”?因为没有人使用它们,当然。但是他们为什么不使用它们呢? ... @Web_Designer 我相信你的评论已经过时了。 MDN 文档中没有任何地方说“避免使用标签”。请考虑修改或删除您的评论。 @SeantheBean 完成。这似乎是更直接的答案,并且不会被滥用,因为它仅适用于continue
和 break
。
@JérémyPouyet - 你反对投票的逻辑是愚蠢和没有根据的。它完美地回答了OP的问题。该问题与您对易读性的看法无关。请重新考虑您协助社区的方式。【参考方案4】:
很简单:
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 循环包装在 if 也不能扩展 ie 使得它难以阅读和调试......这个很棒.您可以只声明 vars loop1、loop2、loop3,并在最后添加小语句。另外要打破多个循环,您需要执行loop1=loop2=false;
之类的操作
我已经使用了这种安排并且它可以工作,而不会因为无用的功能而复杂化。我只是在搜索了一些东西来看看js是否有break 2;
就像在php中一样。【参考方案5】:
嗯,10 岁的聚会你好?
为什么不在你的 for 中设置一些条件?
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 中运行
【讨论】:
【参考方案6】:以下是打破 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();
【讨论】:
@Wyck 我不能同意!遗憾的是,javascript 不像我们在 PHP 中那样简单地具有break 2;
语法。没有循环标签,没有函数,没有 if-else 检查,没有循环变量的回火/爆破——只有干净的语法!
示例 4 很不错
@JayDadhania 抱歉,您的“干净”和“简单”语法给我们的软件带来了错误。显式优于隐式。我想自己命名我的标签。
@EkremDinçel break 2;
这样的语法在 JS 中还不存在,那它怎么会引入错误呢?您想手动标记循环吗?当然,继续 - 我从来没有说过你不应该。另外,我绝不建议使用3,2,1
手动标记 JS 循环 - 到目前为止,JS 不允许仅使用数字手动标记循环。我只是希望这样的东西是隐含的。此外,这样的声明一直是一些非常流行的语言(如 PHP)的核心部分,我还没有遇到(m)任何 “想要手动标记 PHP 循环的帖子,因为 break 2;
很难重新-结构”。
@JayDadhania 它肯定不会在 JS 中引入错误,它不存在。但如果它存在的话,我想我在上面的评论中解释得有点错误。你想要的隐含是一个问题,如果它存在于javascript中,无论你是否建议,人们都会使用它。您以 PHP 作为此语法的示例,我认为您还应该注意到 PHP 的糟糕历史。我听说他们现在正在修复一些东西,但它是一种语言,很长一段时间以来,在用它编写的应用程序中都有如此多的意大利面条代码,这是有原因的。【参考方案7】:
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);
【讨论】:
【参考方案8】:如何将循环推到极限
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;
【讨论】:
我认为Drakes answer的逻辑相同,更简洁明了。 绝对精彩!【参考方案9】:最好的方法是- 1)对第一个和第二个循环中使用的两个数组进行排序。 2) 如果项目匹配,则中断内部循环并保持索引值。 3)当开始下一次迭代时,以保持索引值开始内循环。
【讨论】:
【参考方案10】:我想我会展示一种函数式编程方法。你可以打破嵌套的 Array.prototype.some() 和/或 Array.prototype.every() 函数,就像我的解决方案一样。这种方法的另一个好处是Object.keys()
仅枚举对象自身的可枚举属性,而"a for-in loop enumerates properties in the prototype chain as well"。
接近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;
);
);
【讨论】:
【参考方案11】:如果您使用 Coffeescript,有一个方便的“do”关键字,可以更轻松地定义和立即执行匿名函数:
do ->
for a in first_loop
for b in second_loop
if condition(...)
return
...所以你可以简单地使用“return”来退出循环。
【讨论】:
这不一样。我原来的例子有三个for
循环而不是两个。【参考方案12】:
完全不使用中断,不使用中止标志,也不使用额外的条件检查怎么样。这个版本只是在满足条件时爆破循环变量(使它们成为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
【讨论】:
【参考方案13】:将其包装在一个函数中,然后只需 return
。
【讨论】:
我选择接受这个答案,因为它很简单并且可以以优雅的方式实现。我绝对讨厌 GOTO 并认为它们是不好的做法(可以打开),Ephemient 太接近了。 ;o) IMO,只要不破坏结构,GOTO 就可以。但对每个人来说都是他们自己的! for 循环上的标签与 GOTO 没有绝对没有共同之处,除了它们的语法。它们只是从外部循环中打破的问题。打破最里面的循环没有任何问题,是吗?那么为什么你在打破外循环时遇到问题呢? 请考虑接受其他答案。如果不是因为 Andrew Hedges 的评论(顺便说一句,谢谢),我会想:啊,所以 javascript 没有那个功能。我敢打赌,社区中的许多人可能会忽略评论并认为是一样的。 为什么 Stack Overflow 没有让社区覆盖明显错误的选择答案的功能? ://【参考方案14】:我意识到这是一个非常古老的话题,但由于我的标准方法还没有出现,我想我把它发布给未来的谷歌人。
var a, b, abort = false;
for (a = 0; a < 10 && !abort; a++)
for (b = 0; b < 10 && !abort; b++)
if (condition)
doSomeThing();
abort = true;
【讨论】:
如果在嵌套循环的第一次迭代中condition
的计算结果为 true
,您仍然会运行其余 10 次迭代,每次都检查 abort
值。这不是 10 次迭代的性能问题,但它会是 10,000 次。
不,它从两个循环中退出。这是演示fiddle。不管你设置什么条件,满足后退出。
优化就是加个break;设置 abort = true 后;并从最终循环中删除 !abort 条件检查。
我喜欢这个,但我认为在一般意义上你会做很多不必要的处理——也就是说,对于每个迭代器 evalueate abort
和表达式的每次迭代。在简单的场景中可能没问题,但对于具有无数次迭代的巨大循环可能是个问题
+1 表示一种新颖的方法!但是,这对 for(var a in b)...
或 for(var a of b)...
样式的循环没有帮助。【参考方案15】:
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;
;
【讨论】:
这看起来比原来的更混乱。 投了赞成票,因为一段时间更适合这种情况(在大多数情况下)。【参考方案16】:我参加聚会有点晚了,但以下是一种与语言无关的方法,它不使用 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 人群。不利的一面是,内部循环需要在终止之前完成当前迭代,因此它可能不适用于某些场景。
【讨论】:
左大括号不应该换行,因为js实现可能会在前一行的末尾插入一个冒号。 @Evgeny:虽然一些 JavaScript 样式指南要求左大括号放在同一行,但将它放在新行上并没有错,并且解释器不存在歧义插入分号的危险. ASI 的行为已明确定义,此处不适用。 请务必注释掉这种方法。这里发生了什么并不是很明显。 不错且简单的答案。这应该被视为答案,因为它不会占用 CPU 密集型循环(这是使用函数的问题),或者它不使用通常不可读或不应该像某些人所说的那样使用的标签。 :) 我可能遗漏了一些东西,但是为了解决内部循环必须完成该迭代的问题,您可以在设置 z 和 y 后立即输入break
或 continue
吗?我确实喜欢使用for
循环的条件来退出的想法。以自己的方式优雅。【参考方案17】:
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
怎么样? :)
【讨论】:
我认为这就是 swilliams 的目的 如果循环很大,这会增加显着的运行时成本 - 必须由 Javascript 解释器/编译器(或“compreter”)创建(并在某些时候由 GC 释放)函数的新执行上下文这些天,两者兼而有之)每次。 这实际上是非常危险的,因为可能会发生一些你可能没有预料到的奇怪事情。特别是,由于使用 varx
创建的闭包,如果循环中的任何逻辑在稍后的时间点引用 x(例如,它定义了一个稍后保存并执行的内部匿名函数),x 的值将是循环的 end 中的任何内容,而不是该函数在其期间定义的索引。 (续)
要解决这个问题,您需要将x
作为参数传递给您的匿名函数,以便它创建它的新副本,然后可以引用为关闭,因为从那时起它不会改变。总之,我推荐ehemient的回答。
同样,我认为可读性是完全废话。这比标签更模糊。标签只被视为不可读,因为没有人使用它们。以上是关于打破 JavaScript 中嵌套循环的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
如何打破 Objective-C 中的两个嵌套 for 循环?