【问题标题】:Replacing `for` loop with `forEach` loop用 `forEach` 循环替换 `for` 循环
【发布时间】:2019-04-04 12:13:42
【问题描述】:

我正在研究将任务卡移动到下一列的 StateService 方法。我能够编写工作正常的taskMoveLeft 方法,但我无法使用forEach 循环为taskMoveRight 方法复制它的功能,我只能使用for 循环。

taskMoveLeft 方法的工作示例(使用forEach):

taskMoveLeft(id) {
  state.columns.forEach((column, columnIndex) => {
    if (state.columns[0] !== column) {
      if (column.cards) {
        column.cards.forEach((card, cardIndex) => {
          if (card.id === id) {
            if (state.columns[columnIndex - 1].cards) {
              // Add card to the target column card collection
              state.columns[columnIndex - 1].cards.push(card);
            } else {
              // Create target column card collection and add card
              state.columns[columnIndex - 1].cards = Array.of();
              state.columns[columnIndex - 1].cards.push(card);
            }
            // Remove the card from the source column card collecion
            state.columns[columnIndex].cards.splice(cardIndex, 1);
          }
        });
      }
    }
  });
}

taskMoveRight 方法的工作示例(使用for 循环):

taskMoveRight(id) {
  for (let i = 0; i < state.columns.length; i++) {
    if (state.columns[state.columns.length - 1] !== state.columns[i]) {
      if (state.columns[i].cards) {
        for (let j = 0; j < state.columns[i].cards.length; j++) {
          if (state.columns[i].cards[j].id === id) {
            if (state.columns[i + 1].cards) {
              // Add card to the target column card collection
              state.columns[i + 1].cards.push(state.columns[i].cards[j]);
            } else {
              // Create target column card collection and add card
              state.columns[i + 1].cards = Array.of();
              state.columns[i + 1].cards.push(state.columns[i].cards[j]);
            }
            // Remove the card from the source column card collecion
            return state.columns[i].cards.splice(j, 1);
          }
        }
      }
    }
  }
}

无法使taskMoveRight 方法与forEach 循环一起使用。使用此代码,卡片总是移动到最右列:

taskMoveRight(id) {
  state.columns.forEach((column, columnIndex) => {
    if (state.columns[state.columns.length - 1] !== column) {
      if (column.cards) {
        column.cards.forEach((card, cardIndex) => {
          if (card.id === id) {
            // Create target column card collection
            if (!state.columns[columnIndex + 1].cards) {
              state.columns[columnIndex + 1].cards = Array.of();
            }
            // Add card to the target column card collection
            state.columns[columnIndex + 1].cards.push(card);
            // Remove the card from the source column card collecion
            state.columns[columnIndex].cards.splice(cardIndex, 1);
          }
        });
      }
    }
  });
}

【问题讨论】:

  • 你为什么要 A) 反转 if 条件,以及 B) 删除 else
  • 另请注意,您尝试替换的for 循环在某些情况下会提前结束。您不能提前终止forEach 序列。如果您需要提前终止类似forEach 的事情,请使用some(或every),或者在这种情况下可能使用find
  • 实际上是findIndex
  • 旁注:Array.of(); 是写[] 的一种非常奇怪的方式。 :-)
  • > 你为什么要 A) 反转 if 条件,B) 删除 else?我已经反转它,因为它的代码更少并且逻辑保持不变

标签: javascript arrays loops


【解决方案1】:

forEach 不是正确的工具,因为你提前终止了循环。相反,由于您需要卡片的索引,请使用findIndex。有关该建议和其他建议,请参见 *** cmets:

taskMoveRight(id) {
  for (let i = 0; i < state.columns.length; i++) {
    // *** Instead of repeating it all over
    const column = state.columns[i];
    if (state.columns[state.columns.length - 1] !== column) {
      if (column.cards) {
        // *** Find the card
        const cardIndex = column.cards.findIndex(card => card.id == id);
        // *** Get the card if found
        const card = cardIndex !== -1 && column.cards[cardIndex];
        if (card) {
            // *** Instead of repeating it
            const nextColumn = state.columns[i + 1];
            if (nextColumn.cards) {
              // Add card to the target column card collection
              nextColumn.cards.push(card);
            } else {
              // Create target column card collection and add card
              // *** Using Array.of() to create an empty array and then pushing
              // *** to it is quite round-about; instead: [card]
              nextColumn.cards = [card];
            }
            // Remove the card from the source column card collecion
            return column.cards.splice(cardIndex, 1);
        }
      }
    }
  }
}

【讨论】:

  • 好吧,如果它适用于 taskMoveLeft(),它可能是合适的工具
  • 是的,taskMoveLeft 可以正常工作,问题是如何反转方向并将其用于taskMoveRight
  • 请帮我用forEach方法实现
  • @Jake11 - 不,如果你查看taskMoveLeft,你会看到它在不应该循环的时候一直循环——它已经找到了卡。不是正确的工具。
  • @Bong2000 - 你为什么要继续使用错误的工具来完成这项工作?如果先前的实现确实如此,则上述内容应将列向右移动。如果没有,请使用 minimal reproducible example 更新您的问题,以展示代码,以便可以针对它测试解决方案。
【解决方案2】:

因为您使用当前卡片修改了下一行,然后当您到达下一行时,这些卡片会再次移动。你必须反向迭代,所以你必须这样做而不是.forEach((entry, i)

  array.reverse().forEach((entry, i) => {
    i = array.length - 1 - i;

但这有点难看。


相反,我不会坚持使用.forEach,而是使用 for 循环。我会保留一组条目以向右移动,这样您就可以将逻辑简化为:

  let previousMove = [];

  for(const column of state.columns) {
    if(!column.cards) column.cards = [];
    let currentMove = column.cards.filter(card => card.id === id);
    column.cards = column.cards.filter(card => card.id !== id).concat(previousMove);
    previousMove = currentMove;
  }

这也可以使用reduce

 state.columns.reduce((move, column) => {
    if(!column.cards) column.cards = [];
    const nextMove= coumn.cards.filter(card => card.id === id);
   column.cards = column.cards.filter(card => card.id !== id).concat(move);
   return nextMove;
 }, []);

要向左移动,只需使用reduceRight 而不是reduce

【讨论】: