在ES6的javascript中检测FOR OF循环的最后一次迭代

48

寻找forfor...in循环的最后一次迭代有多种方法。但是我如何在for...of循环中找到最后一次迭代?我在文档中找不到相关信息。

for (item of array) {
    if (detect_last_iteration_here) {
        do_not_do_something
    }
}

3
这并不是完全可能的 - 迭代器可能是无限的。只有在检查下一个元素后才知道它是否是最后一个元素。你具体需要这个做什么呢? - Bergi
11个回答

58

一种方法是使用Array.prototype.entries()

for (const [i, value] of arr.entries()) {
    if (i === arr.length - 1) {
        // do your thing
    }
}

另一种方法是像Shidersz建议的那样,在循环外部保持一个计数。我认为您不希望检查 indexOf(item),因为如果最后一个项在数组中的其他位置重复出现,则会造成问题...


33

一种可能的方法是在循环外初始化一个计数器,并在每次迭代时将其减少:

const data = [1, 2, 3];
let iterations = data.length;

for (item of data)
{
    if (!--iterations)
        console.log(item + " => This is the last iteration...");
    else
        console.log(item);
}
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

注意!--iterations被解析为!(--iterations),当iterations=1时,表达式将为true


8
您可以对数组进行切片,并省略最后一个元素。

var array = [1, 2, 3],
    item;
    
for (item of array.slice(0, -1)) {
    console.log(item)
}


4
OP希望检测最后一个元素,而不是将最后一个元素从数组中删除。 - João Pimentel Ferreira
对于最后一个项目,不应该发生任何事情。但实际上确实发生了。 - Nina Scholz

5

在ES6中,通过调用数组的entries()方法,你可以做到这一点 =>

const array = [1, 2, 3];
for (const [i, v] of array.entries()) {
    //Handled last iteration
    if( i === array.length-1) {
        continue;
    }
    console.log(i, v)// it will print index and value
}

2
一个更加通用的替代方案,基于组合原则,是使用生成器函数实现另一个迭代器。这实际上将组合这两个迭代器。
新的迭代器基本上将实际产生值的工作委托给原始迭代器。区别在于前者始终比后者“领先一步”。通过这样做,新的迭代器将能够验证每个元素是否已经迭代到下一个停止点。
function* withLast(iterable) {
  const iterator = iterable[Symbol.iterator]();
  let curr = iterator.next();
  let next = iterator.next();
  while (!curr.done) {
    yield [next.done, curr.value];
    [curr, next] = [next, iterator.next()];
  }
}

请注意,在第一次迭代中,next 被调用了两次。在随后的每一次迭代中,next 只被调用一次,即针对与当前迭代相应的下一步元素。
了解JavaScript 迭代协议,以了解更多相关实现。
以下是一个更具体的例子:

function* withLast(iterable) {
  const iterator = iterable[Symbol.iterator]();
  let curr = iterator.next();
  let next = iterator.next();
  while (!curr.done) {
    yield [next.done, curr.value];
    [curr, next] = [next, iterator.next()];
  }
}

const arr = [1, 2, 3];
for (const [isLast, element] of withLast(arr)) {
  console.log(`${element} ${isLast ? 'is' : 'is not'} the last.`);
}

作为这个解决方案中没有使用索引或迭代器的实际长度(在某些情况下甚至不是无限迭代器)等概念,因此这种方法非常适合任何类型的迭代器。
另一个例子:

function* withLast(iterable) {
  const iterator = iterable[Symbol.iterator]();
  let curr = iterator.next();
  let next = iterator.next();
  while (!curr.done) {
    yield [next.done, curr.value];
    [curr, next] = [next, iterator.next()];
  }
}

// Generator function that creates a "generic" iterator:
function* gen() {
  yield 'a';
  yield 'b';
  yield 'c';
}

for (const [isLast, element] of withLast(gen())) {
  console.log(`${element} ${isLast ? 'is' : 'is not'} the last.`);
}


1
如果您想基于特定索引更改循环行为,那么在for循环中使用显式索引可能是个好主意。
如果您只想跳过最后一个元素,可以尝试以下做法:
for (item of array.slice(0, -1)) {
    //do something for every element except last
}

1

似乎在for..of的规范中没有找到任何内容。

有两个解决方法:

如果可以的话,只需将一个标记推到数组末尾并在该标记上进行操作,就像这样:

myArray.push('FIN')
for (el of myArray){
    if (el === 'FIN')
        //ending code
}

或者,您可以使用以下方法来获取一个可以与Array.length同时使用的索引。 输入链接描述在此处

0
你还可以使用Array.prototype.at()-1
参考这里的Array.prototype.at()的MDN
你的示例将简单地如下所示:
for (item of array) {
    if (item === array.at(-1)) {
        // do_not_do_something
    }
}

0
最简单的方法是找到最后一次循环,然后比较数组的长度和其最后一个项的索引,这样就可以去掉在迭代过程中插入的最后一个逗号。
const arr = ["verb", "noun", "pronoun"];

for (let item of arr) {
    if (arr.length -1 !== arr.indexOf(item)) {
        console.log('With Commas');
    } else {
        console.log('No Commars');
    }
}

4
这将具有n的阶乘运行时间。 - Gabriel Garrett
2
@GabrielGarrett 我认为它实际上是二次的,但是是的,仍然不太好。然而,这个答案的主要问题是它是错误的。如果数组中有任何重复的值,它将永远无法检测到最后一次迭代。另一方面,如果使用lastIndexOf,即使数组中有重复项,它仍将失败。 - General Grievance

0
const chain = ['ABC', 'BCA', 'CBA'];
let findOut;
for (const lastIter of chain) {
    findOut = lastIter;       //Last iteration value stored in each iteration. 
} 

console.log(findOut);

感谢您尝试回答问题,但如果该问题已经有了几个答案,并且您的答案没有为讨论增添新内容,请将其删除!https://stackoverflow.com/help/how-to-answer - pyeR_biz
这样做的目的是在循环内部检查此内容。这个答案完全忽略了这一点。 - General Grievance

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接