Moving zeros to the end of an array in JavaScript
Published on in JavaScript and TypeScript
Let's learn about anti-symmetry by solving this interview puzzle: "Move all zeros of an array of integers to the end while maintaining the relative order of all non-zeros."
Example:
moveZeros([1, 3, 0, 111, 57, 0, 5, 0, 9])
//=> [1, 3, 111, 57, 5, 9, 0, 0, 0]
Ok, ez because there's a great
visual mnemonic for an array sorter's return values,
so we can simply™ sort()
the values:
const moveZeros = (numbers: number[]) =>
numbers.toSorted((a, b) => (a === 0 ? 1 : 0))
moveZeros([1, 3, 0, 111, 57, 0, 5, 0, 9])
//=> [1, 3, 111, 57, 5, 9, 0, 0, 0]
I used the new toSorted()
method
to avoid mutating the original array.
Seems to work with the test input,
but might break with other inputs,
because the comparator function is not anti-symmetric.
From sort()
on MDN:
[I]f a comparator only returns
1
and0
, or only returns0
and-1
, it will not be able to sort reliably because anti-symmetry is broken.
So we need to also check if b === 0
:
const moveZeros = (numbers: number[]) =>
numbers.toSorted((a, b) => (a === 0 ? 1 : b === 0 ? -1 : 0))
Nested ternaries are icky though,
plus returning 1
or -1
is not very clear without comments.
Final solution:
const moveZeros = (numbers: number[]) =>
numbers.toSorted((a, b) => {
if (a === 0) return +1 // a->
if (b === 0) return -1 // <-a
return 0 // Keep order
})
Though I have to admit that
Juhani Kumara's solution using reduceRight
is annoyingly clever!
const move0 = (arr) =>
arr.reduceRight((acc, cur) => (acc[cur ? 'unshift' : 'push'](cur), acc), [])