I found this interesting boomerang puzzle that many people could get wrong the first time.
Especially if you ask them to do it in one line! — as we going to do later in this article.
const arr = [4, 9, 4, 6, 3, 8, 3, 7, 5, -5, 5];
console.log(countBoomerangs(arr)); // 3
It’s all about counting the “boomerangs” in a list. Can you do it?
Maybe you first want to do what the hell a boomerang is right?
Okay so it’s like, any section of the list with 3 numbers where the first and last digits repeat: like[1, 2, 1]
:
So how many boomerangs can you see in [4, 9, 4, 6, 3, 8, 3, 7, 5, -5, 5]?
…
It’s 3
- [4, 9, 4]
- [3, 8, 3]
- [5, -5, 5]
So the puzzle is to write an algorithm to find this pattern throughout the list.
But here’s where it gets tricky: The algorithm should also count overlapping boomerangs — like in [1, 5, 1, 5, 1, 5, 1] we have FIVE boomerangs — not two — I even thought it was three at first — no it’s freaking five.
So how do we go about this?
My first instinct is to loop through the list and and then when we get up to 3 items we can do the calculation.
It’s one of those situations where we need to keep track of previous values in the loop at every step of the loop.
So in every loop we’ll have one value for the current item, the previous items, and the one before that.
How do we keep track of all the items?
For the current items it’s super easy of course — it’s just the current iter variable:
countBoomerangs([1, 2, 1, 0, 3, 4, 3]);
function countBoomerangs(arr) {
let curr;
for (item of arr) {
curr = item;
console.log(`curr: ${curr}`);
}
}
What about keeping tracking of the previous variable?
We can’t use the current iter variable anymore — we need to store the iter variable from one iteration just before the next one starts.
So what we do — store the value of curr
just before the loop ends:
countBoomerangs([1, 2, 1, 0, 3, 4, 3]);
function countBoomerangs(arr) {
let curr;
let prev;
for (item of arr) {
curr = item;
console.log(`curr: ${curr}, prev: ${prev}`);
prev = curr;
}
}
Or we can actually do it at the point just before the next loop starts:
countBoomerangs([1, 2, 1, 0, 3, 4, 3]);
function countBoomerangs(arr) {
let curr;
let prev;
for (item of arr) {
prev = curr;
curr = item;
console.log(`curr: ${curr}, prev: ${prev}`);
}
}
It has to be these two points — either just before the loop starts or just before it ends.
Just before the loop starts really meaning before we update curr
to the current variable.
Just before it ends really meaning after we finished using the stale prev
variable — before we update it — to be stale again the next iteration — do you get? “Stale” because it’s always going to be out of date, it’s supposed to be.

And what about the previous one before this previous one relative to the current variable? The previous previous variable?
We use the exact same logic — although there’s something you’re going to need to watch out for…
So to track the previous previous variable I need to set prev2
to the stale stale iter variable — if you know what I mean, ha ha…
So like before either at the beginning of the loop like this:
countBoomerangs([1, 2, 1, 0, 3, 4, 3]);
function countBoomerangs(arr) {
let curr;
let prev;
let prev2;
for (item of arr) {
// Stale stale iter variable (stale of the stale)
prev2 = prev;
// Just stale (one stale)
prev = curr;
// Before update to current (non-stale)
curr = item;
console.log(`curr: ${curr}, prev: ${prev}, prev2: ${prev}`);
}
}
But what about at the end?
We have to maintain the order: prev2->prev->curr — we always need to update prev2
‘s to the prev
‘s stale value before update prev
to curr
‘s stale value.
function countBoomerangs(arr) {
let curr;
let prev;
let prev2;
for (item of arr) {
curr = item;
// Stale stale iter variable (stale of the stale)
console.log(`curr: ${curr}, prev: ${prev}, prev2: ${prev}`);
// Before update to current (non-stale)
prev2 = prev;
// Just stale (one stale)
prev = curr;
}
}

So finally we’ve been able to track all these 3 variables to check for the boomerang.
Now all that’s left is to make the check in every loop and update a count — pretty straightforward stuff:
function countBoomerangs(arr) {
// ...
let count = 0;
for (item of arr) {
prev2 = prev;
prev = curr;
curr = item;
if (prev2 === curr && prev !== curr) {
count++;
}
}
return count;
}
console.log(countBoomerangs([1, 2, 1, 0, 3, 4, 3])); // 2
console.log(countBoomerangs([0, 0, 5, 8, 5, 2, 1, 2])); // 2
And it automatically works for overlapping boomerangs too:
console.log(countBoomerangs([1, 5, 1, 5, 1, 5, 1])); // 5
But isn’t there an easier way?
I noticed we could have just use a traditional for loop and use the iter counter get the sub-list up to 3 numbers back:
Either this:
function countBoomerangs2(arr) {
for (let i = 0; i < arr.length; i++) {
const prev2 = arr[i - 2];
const prev = arr[i - 1];
const curr = arr[i];
console.log(
`curr: ${curr}, prev: ${prev}, prev2: ${prev}`
);
}
}
or this:
function countBoomerangs2(arr) {
for (let i = 0; i < arr.length; i++) {
const [prev2, prev, curr] = arr.slice(i - 2, i + 1);
console.log(`curr: ${curr}, prev: ${prev}, prev2: ${prev}`);
}
}

I’ll add a check for when the iter counter goes up to 3 numbers to avoid the undefined
stuff. This would definitely throw a nasty out-of-range error in stricter language like C#.
function countBoomerangs2(arr) {
for (let i = 0; i < arr.length; i++) {
// ✅ Check length
if (arr.length > 2) {
const [prev2, prev, curr] = arr.slice(i - 2, i + 1);
console.log(
}
}
}
And the comparison will be just as before:
function countBoomerangs2(arr) {
let count = 0;
for (let i = 0; i < arr.length; i++) {
if (arr.length > 2) {
const [prev2, prev, curr] = arr.slice(i - 2, i + 1);
if (prev2 === curr && prev !== curr) {
count++;
}
}
}
return count;
}
The best part about this alternative is it lets us set up a beautiful one-liner solution with functional programming constructs like JavaScript’s reduce()
.
function countBoomerangs2(arr) {
return arr.reduce(
(count, _, i, arr) =>
arr[i - 2] === arr[i] && arr[i - 1] !== arr[i]
? count + 1
: count,
0
);
}
Short and sweet.
Okay maybe not so short — but now just one single multi-line statement.
Puzzle solved.