ChatGPT解决这个技术问题 Extra ChatGPT

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

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

e
ephemient

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


@NielsBom您是对的:它以给定的标签打破了循环。它没有 goto 标签。你本质上是在命名一个循环并说我想打破这样那样的循环。
WTF 为什么我在 3 年的 JavaScript 中没有看到它在某个地方被使用过:/..
MDN 说“避免使用标签”纯粹是出于可读性的原因。为什么它不“可读”?因为没有人使用它们,当然。但是他们为什么不使用它们呢? ...
@SeantheBean 完成。这似乎是更直接的答案并且不会被滥用,因为它仅适用于 continuebreak
@JérémyPouyet - 你反对投票的逻辑是愚蠢和没有根据的。它完美地回答了OP的问题。该问题与您对易读性的看法无关。请重新考虑您协助社区的方法。
C
CloudyMarble

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


我选择接受这个答案,因为它很简单并且可以以优雅的方式实现。我绝对讨厌 GOTO 并认为它们是不好的做法(可以打开),Ephemient 太接近了。 ;o)
IMO,只要不破坏结构,GOTO 就可以了。但各有各的!
for 循环上的标签与 GOTO 完全没有共同之处,除了它们的语法。它们只是从外部循环中打破的问题。打破最里面的循环没有任何问题,是吗?那么为什么你有打破外循环的问题呢?
请考虑接受其他答案。如果不是因为 Andrew Hedges 的评论(顺便说一句,谢谢),我会想:啊,所以 javascript 没有那个功能。我敢打赌社区中的许多人可能会忽略评论并认为相同。
为什么 Stack Overflow 没有让社区覆盖明显错误的选择答案的功能? :/
a
aleemb

我参加聚会有点晚了,但以下是一种与语言无关的方法,它不使用 GOTO/labels 或函数包装:

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 的行为已明确定义,此处不适用。
只要确保对这种方法进行评论即可。这里发生了什么并不是很明显。
我可能遗漏了一些东西,但是为了解决内部循环必须完成该迭代的问题,您可以在设置 z 和 y 后立即放入 breakcontinue 吗?我确实喜欢使用 for 循环的条件来退出的想法。以自己的方式优雅。
+1 一种新颖的方法!但是,这对 for(var a in b){...}for(var a of b){...} 样式的循环没有帮助。
z
zord

我意识到这是一个非常古老的话题,但由于我的标准方法还没有出现,我想我把它发布给未来的谷歌人。

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。不管你设置什么条件,满足后就会退出。
优化将是添加一个休息;设置 abort = true 后;并从最终循环中删除 !abort 条件检查。
我喜欢这个,但我认为在一般意义上你会做很多不必要的处理——也就是说,对于每个迭代器 evalueate abort 和表达式的每次迭代。在简单的场景中可能没问题,但是对于具有无数次迭代的巨大循环可能是个问题
+1 一种新颖的方法!但是,这对 for(var a in b){...}for(var a of b){...} 样式的循环没有帮助。
a
akinuri

非常简单:

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 是否像 php 中的 break 2; 后才到达这里。
h
harley.333
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 解释器/编译器(或者,如今的“解释器”,两者的混合)创建函数的新执行上下文(并在某些时候由 GC 释放)每一次。
这实际上是非常危险的,因为可能会发生一些您可能没有预料到的奇怪事情。特别是,由于使用 var x 创建的闭包,如果循环中的任何逻辑在稍后的时间点引用 x(例如,它定义了一个稍后保存并执行的内部匿名函数),x 的值将是循环的 end 中的任何内容,而不是该函数在其期间定义的索引。 (续)
为了解决这个问题,您需要将 x 作为参数传递给匿名函数,以便它创建它的新副本,然后可以将其作为闭包引用,因为它不会更改从那时起。总之,我推荐ehemient的答案。
同样,我认为可读性完全是废话。这比标签更模糊。标签仅被视为不可读,因为没有人使用它们。
D
Dan Bray

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

1) 将 parent(s) 循环设置到末尾

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 JS 中还没有像 break 2; 这样的语法,那么它怎么会引入错误呢?您想手动标记循环吗?当然,继续 - 我从来没有说过你不应该。此外,我绝不建议 手动标记 JS 循环与3,2,1 - JS 不允许手动标记循环现在只有数字。我只是希望这样的东西是隐含的。此外,这样的声明一直是一些非常流行的语言(如 PHP)的核心部分,而且我没有遇到 (m) 任何 “想要手动标记 PHP 循环的帖子,因为 break 2; 很难重新-结构”。
@JayDadhania 它肯定不会在 JS 中引入错误,它不存在。但如果它存在的话,我想我在上面的评论中解释得有点错误。你想要的隐含是一个问题,如果它存在于javascript中,无论你是否建议,人们都会使用它。您以 PHP 作为此语法的示例,我认为您还应该注意到 PHP 的糟糕历史。我听说他们现在正在修复一些东西,但它是一种语言,长期以来在用它编写的应用程序中都有如此多的意大利面条代码,这是有原因的。
D
Drakes

完全不使用中断,不使用中止标志,也不使用额外的条件检查怎么样。这个版本只是在满足条件时爆破循环变量(使它们成为 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
        }
      }
    }
  }
}

N
Nick Perkins

如果您使用 Coffeescript,有一个方便的“do”关键字可以更轻松地定义和立即执行匿名函数:

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

...所以你可以简单地使用“return”来摆脱循环。


这不一样。我原来的例子有三个 for 循环而不是两个。
u
user889030

如何将循环推到极限

    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 以更简洁明了的方式具有相同的逻辑。
绝对精彩!
Z
Zachary Ryan Smith

我想我会展示一种函数式编程方法。你可以打破嵌套的 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;
            }
        });
    });

M
Matt Borja

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

A
Azutanguy

嗯,嗨到 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 中运行


T
Tom Chen

上面有很多优秀的解决方案。 IMO,如果您的中断条件是异常的,您可以使用 try-catch:

try{  
    for (var i in set1) {
        for (var j in set2) {
            for (var k in set3) {
                throw error;
            }
        }
    }
}catch (error) {

}

P
Pratik Khadtale

分配处于比较条件的值

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

}


T
Timo

一个带有 for .. of 的例子,靠近上面的例子,它检查中止条件:

test()
function test() {
  var arr = [1, 2, 3,]
  var abort = false;
  for (var elem of arr) {
    console.log(1, elem)

    for (var elem2 of arr) {
      if (elem2 == 2) abort = true;  
        if (!abort) {
            console.log(2, elem2)
        }
    }
  }
}

条件 1 - 外循环 - 将始终运行

最高投票和接受的答案也适用于这种 for 循环。

结果:内部循环将按预期运行一次

1 1
2 1
1 2
1 3

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

这看起来比原来的更混乱。
投票是因为一段时间更适合这种情况(在大多数情况下)。
D
Deepak Karma

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