DEV Community

Cover image for Can you spot the bug? Every bucket in this TypeScript function comes out identical
RSZ
RSZ

Posted on

Can you spot the bug? Every bucket in this TypeScript function comes out identical

I've started collecting one specific kind of bug: the ones where every line is correct on its own and the whole thing is still broken. This little TypeScript partition is one of my favorites.

It's meant to split items into N buckets, round-robin. First item into bucket 0, second into bucket 1, then it wraps back around.

// Split items into `groupCount` round-robin buckets.
function partition<T>(items: T[], groupCount: number): T[][] {
  const buckets: T[][] = Array(groupCount).fill([]);
  items.forEach((item, i) => {
    buckets[i % groupCount].push(item);
  });
  return buckets;
}

const result = partition(["a", "b", "c", "d"], 2);
console.log(result[0]); // expected: ["a", "c"]
console.log(result[1]); // expected: ["b", "d"]
Enter fullscreen mode Exit fullscreen mode

Looks right to me. The index math is correct and % groupCount wraps around exactly like it should.

Then you run it. Both logs print ["a", "b", "c", "d"]. Every bucket holds the whole list.

And the part that earns it a spot in the collection: it's not that the items went to the wrong bucket. result[0] and result[1] are the same array. Push to one, the other changes too.

Don't scroll yet. Take ten seconds first. One line is doing all the damage.

Got a guess? Drop it in the comments before you read on. The answer's right below.


The answer

It's this line:

const buckets: T[][] = Array(groupCount).fill([]);
Enter fullscreen mode Exit fullscreen mode

.fill([]) doesn't hand each slot its own array. It takes the one [] you passed and writes that same reference into every index. So buckets[0] === buckets[1], and every .push ends up in a single shared array.

Give each slot its own:

const buckets: T[][] = Array.from({ length: groupCount }, () => []);
Enter fullscreen mode Exit fullscreen mode

The callback runs once per slot, so each gets a fresh [].

What makes this one mean is how quietly it passes. Review waves it through. A smoke test that only checks result.length stays green. Then weeks later something writes to one bucket and a different one changes, and you're staring at a stack trace that points nowhere near partition.

I got obsessed enough with bugs like this that I built a thing around them, OneBug. You drop a snippet like this into a post with one script tag, and the reader gets a timer and has to click the bad line before it runs out.

The same partition puzzle, live in OneBug - timer running, no spoilers

Spoiler-free, no signup. Disclosing it because it's mine, not because I'm pitching: onebug.dev.

Anyway, what's the worst "looks like a copy, turns out it's the same reference" bug you've shipped? I want them for the collection.

Top comments (0)