- Home
- /
- Tutorials
- /
- DSA Tutorial
- /
- Array Traversal Patterns
Arrays
Array Traversal Patterns
Most array problems are one of four walks. Recognising which one you need is faster than inventing a solution from scratch.
1. Single pass with running state
Carry one value through the array and update it as you go. Works whenever the answer depends only on what you have already seen.
Running total and best run
javascript
function largest(items) {
let best = items[0]
for (const n of items) {
if (n > best) best = n
}
return best
}
// Largest sum of any run of consecutive numbers.
function bestRun(items) {
let best = items[0]
let current = items[0]
for (let i = 1; i < items.length; i++) {
current = Math.max(items[i], current + items[i])
best = Math.max(best, current)
}
return best
}
console.log(bestRun([-2, 1, -3, 4, -1, 2, 1, -5, 4])) // 6bestRun is worth reading twice. The trick is that you never need the whole array - only the best run ending at the current position.
2. Two indices from both ends
Start outside and walk inward. Needs the array sorted, or the problem to be symmetric.
Both ends
javascript
function isPalindrome(items) {
let left = 0
let right = items.length - 1
while (left < right) {
if (items[left] !== items[right]) return false
left++
right--
}
return true
}
console.log(isPalindrome([1, 2, 1])) // true
console.log(isPalindrome([1, 2, 3])) // false3. Two indices moving forward
One pointer reads, the other writes. This is how you rearrange in place without allocating.
Remove in place
javascript
// Strip out every zero without creating a new array.
function removeZeros(items) {
let write = 0
for (let read = 0; read < items.length; read++) {
if (items[read] !== 0) {
items[write] = items[read]
write++
}
}
items.length = write
return items
}
console.log(removeZeros([0, 1, 0, 3, 12])) // [1, 3, 12]4. Remember what you have seen
When the answer depends on a value appearing earlier, store it in a Set or Map rather than scanning backwards.
Seen before
javascript
function firstRepeat(items) {
const seen = new Set()
for (const n of items) {
if (seen.has(n)) return n
seen.add(n)
}
return null
}
console.log(firstRepeat([1, 2, 3, 2])) // 2
console.log(firstRepeat([1, 2, 3])) // nullChoosing between them
- Answer depends only on what came before - single pass.
- Array is sorted and you want a pair - both ends.
- Modifying in place without extra memory - read and write pointers.
- Need to know if something appeared earlier - a Set or Map.
Walking backwards
When you remove elements while looping, going forwards skips items: removing index 2 shifts index 3 into its place, and the loop then moves to index 3, stepping over it. Iterating backwards avoids the problem entirely because the indices you have not visited yet never move.
Removing while iterating
javascript
const items = [1, 2, 2, 3, 2]
// Wrong — skips the second 2 of each pair.
for (let i = 0; i < items.length; i++) {
if (items[i] === 2) items.splice(i, 1)
}
// Right — later indices are untouched by earlier removals.
const values = [1, 2, 2, 3, 2]
for (let i = values.length - 1; i >= 0; i--) {
if (values[i] === 2) values.splice(i, 1)
}
console.log(values) // [1, 3]Choosing the loop itself
forwith an index - when you need the index, or must go backwards.for...of- when you only need values. Works on strings, Maps and Sets too.forEach- readable, but you cannotbreakout of it.some/every- an early exit with a boolean result, which is the readable way to say "stop when found".
In an interview, a plain indexed for loop is never the wrong answer. The functional methods are clearer to read but allocate and cannot short-circuit, which matters once n is large.
