Python vs JavaScript Loops
Iteration patterns, comprehensions, and functional alternatives across Python and JavaScript.
For Loop Basics
Python's for loop iterates directly over any iterable -- lists, strings, dictionaries, generators, file handles. There is no index counter unless you ask for one. JavaScript offers three flavors: the C-style for (let i = 0; ...), for...in (which iterates over object keys and is generally avoided for arrays), and for...of (ES6, which iterates over iterable values like Python's for). The mental model is that Python defaults to value iteration and makes you opt in to indices, while JavaScript's history started with index-based loops and added value iteration later. This history matters because for...in on an array iterates over string keys ("0", "1", "2"), not numeric indices, and it walks the prototype chain. Enough production bugs trace back to for...in on arrays that most style guides ban it for that purpose. Python's for-in has no such footgun -- it always yields values in insertion order for lists and dictionaries (3.7+). When you need both the index and the value, the two languages diverge in ergonomics: Python's enumerate() is built-in and idiomatic, while JavaScript's .entries() on arrays returns [index, value] pairs through for...of.
Iterate over a list/array
colors = ["red", "green", "blue"]
for color in colors:
print(color)const colors = ["red", "green", "blue"];
for (const color of colors) {
console.log(color);
}Index-based iteration
for i, color in enumerate(colors):
print(f"{i}: {color}")for (const [i, color] of colors.entries()) {
console.log(`${i}: ${color}`);
}Can you write this from memory?
Iterate over items and print each item
Comprehensions vs Functional Array Methods
List comprehensions are one of Python's signature features. They replace the pattern of initializing an empty list, looping, and appending with a single expression: [x * 2 for x in items if x > 0]. JavaScript has no comprehension syntax. The equivalent is a chain of .filter() and .map(): items.filter(x => x > 0).map(x => x * 2). Both approaches produce a new array without mutating the original, but comprehensions read left-to-right in iteration order (source, transform, filter), while the method chain reads in processing order (filter first, then map). Developers switching between the two often find one ordering more natural than the other, and the preference tends to stick. Performance-wise, Python comprehensions are faster than equivalent for-loops because the append happens in C under the hood. JavaScript's .map() and .filter() each produce an intermediate array, so chaining three methods means three full passes over the data. For small arrays that difference is noise, but for large datasets it tips the scale toward a single .reduce() or a for...of loop in JavaScript, just as a generator expression (round parentheses instead of square brackets) is the memory-efficient alternative in Python. Dictionary comprehensions ({k: v for k, v in pairs}) have no JavaScript equivalent either -- you use Object.fromEntries() with a mapped array of [key, value] pairs, which is functional but verbose.
Filter and transform
nums = [1, -2, 3, -4, 5]
pos_doubled = [n * 2 for n in nums if n > 0]
# => [2, 6, 10]const nums = [1, -2, 3, -4, 5];
const posDoubled = nums
.filter(n => n > 0)
.map(n => n * 2);
// => [2, 6, 10]Dictionary / object from pairs
pairs = [("a", 1), ("b", 2)]
d = {k: v for k, v in pairs}
# => {"a": 1, "b": 2}const pairs = [["a", 1], ["b", 2]];
const obj = Object.fromEntries(pairs);
// => { a: 1, b: 2 }Can you write this from memory?
Iterate over items and print each item
While Loops and Break/Continue
While loops are nearly identical in both languages: check a condition, execute the body, repeat. The syntax differs (Python uses a colon and indentation, JavaScript uses braces), but the semantics match. Where they diverge is Python's for...else and while...else construct. An else block on a loop runs only when the loop completes without hitting a break. This is a niche feature with no JavaScript equivalent, but it elegantly replaces the "found" flag pattern. If you need to search a collection and act when nothing matches, Python's for...else avoids the extra boolean. JavaScript developers solve the same problem with Array.prototype.some(), .find(), or .includes(), which are arguably more readable for simple lookups but cannot replace the full generality of for...else with nested breaks. Break and continue work identically in both languages for simple loops. JavaScript adds labeled breaks (outer: for ...) that let you exit nested loops by name, while Python has no labeled breaks -- you use a flag variable, a function with an early return, or itertools-based approaches to exit multiple levels.
While loop
count = 0
while count < 5:
print(count)
count += 1let count = 0;
while (count < 5) {
console.log(count);
count++;
}for...else vs find
for item in inventory:
if item.name == "key":
use(item)
break
else:
print("No key found")const item = inventory.find(
i => i.name === "key"
);
if (item) {
use(item);
} else {
console.log("No key found");
}Iterators and Generators
Both languages support generators -- functions that yield values lazily instead of returning a complete collection. Python generators use yield inside a regular function, and calling the function returns a generator object. JavaScript generators use function* and yield. The syntax is cosmetically different but the behavior aligns: each call to next() (or next(value) in JS) resumes execution from the last yield. Where the ecosystems diverge is in how deeply generators are integrated into the language. Python builds heavily on the iterator protocol. Dictionaries, files, range objects, zip(), map(), and filter() all return iterators. The entire for-loop mechanism calls __iter__ and __next__ under the hood. The itertools standard library module provides dozens of combinators (chain, product, combinations, groupby) that compose lazily. JavaScript's iteration protocol (Symbol.iterator) is younger and less pervasive. Arrays, strings, Maps, and Sets are iterable, but plain objects are not -- you need Object.entries() or Object.keys() to iterate them. The generator-based library ecosystem in JavaScript (e.g., iter-tools, IxJS) exists but is not part of the standard library, so most codebases reach for .map()/.filter() chains instead of lazy pipelines. The upcoming Iterator Helpers proposal (stage 3) aims to close this gap with built-in .map(), .filter(), and .take() on iterators.
Generator function
def countdown(n):
while n > 0:
yield n
n -= 1
for val in countdown(3):
print(val) # 3, 2, 1function* countdown(n) {
while (n > 0) {
yield n;
n--;
}
}
for (const val of countdown(3)) {
console.log(val); // 3, 2, 1
}Lazy range
# range() is lazy by default
total = sum(range(1_000_000))// No built-in lazy range
function* range(n) {
for (let i = 0; i < n; i++) yield i;
}
let total = 0;
for (const i of range(1_000_000)) total += i;