The LeetCode Zigzag Conversion Problem in TypeScript

In Brief
The clearest Zigzag Conversion solution usually keeps one string per row, walks the input once, and reverses direction at the first and last row. Handle a single row and inputs shorter than the row count before starting. Mathematical indexing can use less intermediate state, but it is harder to read; for an interview solution, a small direction‑controlled simulation is often the better trade‑off.
Strings turn up throughout front‑end work, from user input and JSON responses to text formatting. The Zigzag Conversion problem takes that familiar material and turns it into an indexing exercise: the difficulty is placing each character in the correct row.
What is the Zigzag Conversion Problem?
The Zigzag Conversion problem is a classic coding interview challenge. Given a string and a number of rows, your task is to write the string in a zigzag pattern across the specified number of rows and then return the result as a single string read row by row.
For example, the string ZIGZAGCONVERSION when written in a zigzag pattern across three rows would look like this:

And in code, would look like this:
Z A N S
I Z G O V R I N
G C E OWhen read row‑by‑row, the result is ZANSIZGOVRINGCEO.
To offer another example, this the string PAYPALISHIRING (which I've taken directly from the LeetCode problem page), also over three rows:
P A H N
A P L S I I G
Y I RWhen reading this row‑by‑row, the result is PAHNAPLSIIGYIR.
It potentially sounds more complex than it actually is, but the complexity comes from increasing the number of rows; as the number of rows increases, so does the complexity of indexing each character correctly.
Approaches to a Solution
There are two algorithms worth comparing here: simulate the rows as we read the string, or calculate which original character indices belong to each output row. We can also vary how we store the simulated rows:
- Row simulation: move down and up through the rows, collecting characters in each one.
- Direct indexing: use the repeating cycle to visit the original characters in output order.
- Storage variant: collect each simulated row as a string or as an array of characters. This changes the representation, not the underlying algorithm.
1. Iteration with Direction Control
The idea here is to iterate over the string character by character and place each character into the appropriate row. We control the direction we move in (either up or down) based on our current position.
The Code
In JavaScript (TypeScript), this would be fairly simple and would look like this:
const convert = (s: string, numRows: number): string => {
if (numRows === 1) return s;
const rows: string[] = new Array(Math.min(numRows, s.length)).fill('');
let currentRow: number = 0;
let goingDown: boolean = false;
for (const char of s) {
rows[currentRow] += char;
if (currentRow === 0 || currentRow === numRows - 1) goingDown = !goingDown;
currentRow += goingDown ? 1 : -1;
}
return rows.join('');
};How It Works
We initialise an array of rows as strings. As we loop through the characters in the input string, we append each character to its respective row in the rows array. We then change our direction (going up or down) whenever we reach the top or bottom row.
To return the result, we join those row strings together.
2. Visit Characters in the Original String
This method is more direct. Instead of placing characters into rows as we go, we can calculate the exact interval between characters in the original string, for each row of the zigzag pattern, and visit them directly.
The Code
const convertDirect = (s: string, numRows: number): string => {
if (numRows === 1) return s;
const len: number = s.length;
const cycleLen: number = 2 * numRows - 2;
let result: string = '';
for (let i = 0; i < numRows; i++) {
for (let j = 0; j + i < len; j += cycleLen) {
result += s[j + i];
if (i != 0 && i != numRows - 1 && j + cycleLen - i < len) {
result += s[j + cycleLen - i];
}
}
}
return result;
};How It Works
Instead of just working our way through the input pushing each character into a row array, here we first determine the "cycle" of characters in our zigzag pattern. Then, by iterating through our rows and cycles, we can pick out each character from our original string that belongs in the final result.
Row Simulation with Character Arrays
The first solution stored each row as a string. This variant uses an array of characters for each row, then joins the rows at the end. These are compact row buffers: they do not include the empty positions needed to draw the zigzag grid.
The Code
Here is the same direction‑controlled simulation using arrays:
const convertUsing2D = (s: string, numRows: number): string => {
if (numRows === 1 || s.length <= numRows) return s;
// Initialise 2D matrix
const matrix: string[][] = Array.from({ length: numRows }, () => []);
let row: number = 0;
let direction: 'down' | 'up' = 'down';
for (const char of s) {
matrix[row].push(char);
// Decide direction
if (row === 0) {
direction = 'down';
} else if (row === numRows - 1) {
direction = 'up';
}
row += direction === 'down' ? 1 : -1;
}
// Convert 2D matrix to string
let result: string = '';
for (const line of matrix) {
result += line.join('');
}
return result;
};How It Works
- Create an array containing one empty character array per row.
- Append each character to the current row.
- Reverse direction at the first and last rows.
- Join the characters in each row, then concatenate those row strings.
This representation can make it convenient to inspect each row's collected characters. It does not draw the diagonal spacing of the original zigzag, and its memory and runtime costs should be measured before treating it as an optimisation.
Comparison and Conclusion
Row simulation is straightforward to follow: choose a row, append a character, and reverse direction at either end. String and array storage are two ways to express that same approach.
Direct indexing uses the repeating cycle to avoid collecting separate rows. It asks us to reason more carefully about the first, last and middle rows; that is the main trade‑off to consider when choosing an interview solution.
All three examples visit each input character once for the problem's ordinary string inputs. Actual speed depends on string and array operations in the runtime, so the absence of row buffers alone is not proof of a faster implementation.
Obviously, the likelihood of a web developer coming across this problem as a genuine, real‑world requirement (rather than something an interviewer might present to you) is very slim. However, the Zigzag Conversion problem, whilst a coding challenge at its heart, underlines the importance of string manipulation, a cornerstone in the domain of web applications.
Track the current row and reverse direction at the first and last rows, appending each character as you go. Join the rows at the end. This direct simulation is O(n), handles the one‑row case explicitly, and is easier to verify than trying to derive every character index up front.