A single loop iterating one collection but doing multiple unrelated jobs — accumulating a sum, finding a minimum, building a derived list, all interwoven in the loop body.
Each loop does one thing; mixed-purpose loops separate into named single-purpose passes.
Before the refactoring
let totalSalary = 0;let youngest = Infinity;for (const p of people) {if (p.age < youngest) youngest = p.age;totalSalary += p.salary;}
After the refactoring
const totalSalary = people.reduce((s, p) => s + p.salary, 0);const youngest = Math.min(...people.map(p => p.age));
Each line of the body could touch any of the loop's concerns; reasoning about one purpose requires holding all of them; bugs hide where concerns share an iteration variable.
Two loops over the same collection are slower than one — only split when the doubled cost is dwarfed by the readability gain (which it usually is).
Each loop can then be replaced by a pipeline or extracted by name; bugs concentrate in one purpose at a time.
Splitting a loop whose concerns genuinely share state — running total AND previous value, where each iteration depends on the previous — fragments coupled logic into pieces that have to re-derive shared state.