Який найкращий спосіб перерватися з вкладених циклів у 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.
         }
      }
   }
}

Ось хороший приклад виривання
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
            }
        }
    }

як визначено в розділі 12.12 EMCA-262. [Документи MDN]

На відміну від C, ці мітки можуть використовуватися лише для, continueа breakу Javascript немає goto.


387
WTF, чому я не бачив, щоб це використовувалося десь у мої 3 роки з JavaScript: / ..
Salman von Abbas

39
MDN каже, що "уникайте використання міток" виключно з ознак читабельності. Чому він не "читабельний"? Тому що їх ніхто, звичайно, не використовує. Але чому вони не використовують їх? ...
XML

7
@Web_Designer Я вважаю, що ваш коментар застарів. Ніде в документах MDN не сказано "уникати використання міток". Будь ласка, перегляньте або видаліть свій коментар.
Шон Бін

8
@SeantheBean Готово. Це здається більш простою відповіддю і не піддається зловживанням, оскільки це доступно лише для continueта break.
Gary Willoughby

29
@ JérémyPouyet - Ваша логіка прихильного голосування є неправомірною та необґрунтованою. Це чудово відповідає на питання ОП. Питання не стосується вашої думки щодо розбірливості. Будь ласка, перегляньте свій підхід до надання допомоги громаді.
Дембінський

168

Заверніть це у функції, а потім просто return.


12
Я вирішу прийняти цю відповідь, тому що вона проста і може бути виконана елегантно. Я абсолютно ненавиджу GOTO і вважаю їх поганою практикою ( може відкритися ), Ephemient є занадто близькою. ; o)
Gary Willoughby

16
IMO, GOTO - це добре, якщо вони не порушують структурування. Але кожному своє!
ефемієнт

31
Мітки на циклах абсолютно нічого не мають спільного з GOTO, крім їх синтаксису. Вони просто питання, щоб відірватися від зовнішніх петель. У вас немає жодних проблем із розривом найпотаємнішої петлі, чи не так? так чому у вас виникають проблеми з розривом зовнішніх петель?
Джон Сміт

11
Просимо прийняти іншу відповідь. Якби не коментар Ендрю Хеджеса (спасибі досі), я б подумав: ах, тому javascript не має цієї функції. І я сподіваюся, що багато хто в громаді можуть не помітити коментарів і подумати так само.
Джон Сміт

11
Чому переповнення стека не має функції, яка дозволить спільноті перекрити очевидно неправильну обрану відповідь? : /
Метт Х'юггінс

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

Нагорі воно тече природним шляхом, що повинно радувати нелюдського натовпу. З іншого боку, внутрішній цикл повинен завершити поточну ітерацію перед завершенням, щоб це не було застосовано в деяких сценаріях.


2
вступна дужка не повинна знаходитись у нових рядках, оскільки реалізація js може вставити двокрапку в кінці попереднього рядка.
Євгеній

21
@Evgeny: хоча деякі посібники зі стилю JavaScript закликають відкрити дужки переходити на ту саму лінію, неправильно розміщувати її на новому рядку, і немає небезпеки, щоб перекладач неоднозначно вставив крапку з комою. Поведінка ASI чітко визначена і тут не поширюється.
Джейсон Суарес

9
Просто переконайтесь, що прокоментуєте цей підхід. Не відразу очевидно, що тут відбувається.
Qix - МОНІКА ПОМИЛИЛА

1
Приємна і проста відповідь. Це слід розглядати як відповідь, оскільки це не напружує інтенсивні петлі процесора (що є проблемою використання функцій) або не використовує мітки, які зазвичай не читаються або не повинні використовуватися як кажуть деякі. :)
Girish Sortur

2
Можливо, мені чогось не вистачає, але щоб подолати проблему внутрішнього циклу, який повинен закінчити цю ітерацію, чи можете ви поставити її a breakабо continueвідразу після встановлення z і y? Мені подобається ідея використання умов forциклу для виходу з ладу. Елегантний по-своєму.
Бен Саттон

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
Оптимізацією було б додати перерву; після встановлення abort = true; та усунення! перевірка стану перерви з остаточного циклу.
xer21

1
Мені це подобається, але я думаю, що в загальному сенсі ви зробите багато непотрібної обробки - тобто для кожної ітерації кожного ітератора евакуюють abortі вираз. У простих сценаріях це може бути добре, але для величезних циклів із газильйонними ітераціями, які можуть стати проблемою
З. Хулла

1
Ви справді сперечаєтесь про те, чи перевірка одного булевого значення 10000 разів швидка чи повільна? спробуйте 100 мільйонів разів /
зітхніть

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 отримував у
harley.333

18
Це додає значних витрат на виконання, якщо цикл великий - новий контекст виконання функції повинен бути створений (і в якийсь момент звільнений GC) інтерпретатором / компілятором Javascript (або "компретером" в ці дні, поєднанням обох) ВСЕ ОДИН ЧАС.
Мерре

2
Це насправді досить небезпечно, оскільки можуть статися якісь дивні речі, яких ви можете не очікувати. Зокрема, через закриття, створене з var x, якщо будь-яка логіка в циклі посилається на x на пізній час (наприклад, вона визначає внутрішню анонімну функцію, яка зберігається та виконується пізніше), значення для x буде будь-яким знаходився в кінці циклу, а не індекс, який функціонував під час. (продовження)
devios1

1
Щоб обійти це, вам потрібно передати xяк параметр вашій анонімній функції так, щоб вона створила нову його копію, на яку потім можна посилатися як на закриття, оскільки з цього моменту вона не зміниться. Коротше кажучи, рекомендую відповідь ефемії.
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;
Мухаммед Умер

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
вишуканий

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
Я думаю, що відповідь Дрейкса має ту саму логіку більш лаконічно і зрозуміло.
Інженер Тост

абсолютно геніально!
geoyws

3

Якщо ви використовуєте Coffeescript, є зручне ключове слово "зробити", яке полегшує визначення та негайне виконання анонімної функції:

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

... так що ви можете просто використовувати "return", щоб вийти з циклів.


Це не те саме. Мій оригінальний приклад має три forпетлі, а не дві.
Gary Willoughby

2

Я думав, що я покажу функціонально-програмовий підхід. Ви можете вибивати з вкладених функцій Array.prototype.some () та / або Array.prototype.every (), як у моїх рішеннях. Додатковою перевагою цього підходу є те, що Object.keys()перераховується лише власні перелічені властивості об'єкта, тоді як «цикл для входу перераховує також властивості в ланцюзі прототипу». .

Близьке до рішення ОП:

    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
Це виглядає більш заплутано, ніж оригінал.
Кріштіану Фонтес

21
Як постмодерністська поема
Дігеркам

Голосували за те, що певний час все більше перетворюється на цей тип сценарію (у більшості випадків).
Коді

-4

найкращий спосіб -
1) Сортувати обидва масиви, які використовуються у першому та другому циклі.
2) якщо елемент збігається, тоді перервіть внутрішній цикл і утримуйте значення індексу.
3) при наступній ітерації запустіть внутрішній цикл зі значенням індексу утримування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.