Shape Rule
Fixed start, shrinking stop
Row 1 prints 54321, row 2 prints 5432, shrinking until a single 5 — every row starts at rows.

The left-aligned descending number triangle prints 54321, 5432, 543, 54, 5 — a natural step after the reverse descending triangle in Program 3. This tutorial covers a fixed inner start with shrinking stop, nested loops, a live preview, algorithm steps, worked JavaScript examples, edge cases, and complexity.
Fixed start, shrinking stop
Row 1 prints 54321, row 2 prints 5432, shrinking until a single 5 — every row starts at rows.
0..rows-1
for (let i = 0; i < rows; i++) changes the inner loop’s stop point each row.
rows..i descending
for (let j = rows; j > i; j--) always starts at rows and counts down.
Same line / next line
Digits use line += j; end each row with console.log(line).
3–9 rows
Pick a row count and draw the left-aligned descending triangle in the browser.
Complexity
Total digit prints = n(n+1)/2; extra memory stays O(1).
A left-aligned descending number triangle prints every row starting from rows and counting down, but each next row stops earlier. With rows = 5, the output is 54321, 5432, 543, 54, 5.
In JavaScript the outer loop runs i = 0..rows-1, the inner loop appends j from rows down to i+1 via for (let j = rows; j > i; j--), then console.log(line) moves to the next line.
It teaches fixed-start inner loops with a changing stop — a key step after Program 3’s reverse descending rows.
Inner loop always begins at rows.
Outer loop changes where the inner loop stops.
Program 3 shifts the start each row; Program 4 keeps the same first digit.
Follow Program 3; continue to Program 5 (ascending triangle) next.
In short: for each i from 0 to rows-1, append j from rows down to i+1, then console.log(line).
Given a positive integer rows (e.g. 5), print a left-aligned descending triangle: each row starts at rows and counts down, with the outer loop shortening the stop point each line.
// rows = 5 (conceptual shape)
// 54321
// 5432
// 543
// 54
// 5 | Item | Type | Description |
|---|---|---|
rows | number | Maximum digit and number of triangle lines. |
i | number | Outer loop — row index from 0 to rows-1; controls inner stop. |
j | number | Inner loop — descending from rows down to i+1. |
for i from 0 to rows-1:
for j from rows down to i+1: append j to line
console.log(line) | Approach | Idea | Best for |
|---|---|---|
| Nested loops | 54321, 5432, … | Learning and interviews |
| User-input rows | parseInt(prompt(), 10) | Flexible console programs |
| Spaced output | line += j + " " | Easier reading per row |
| Goal | Pattern |
|---|---|
| Walk rows | for (let i = 0; i < rows; i++) |
| Append digits rows..i+1 | for (let j = rows; j > i; j--) line += j |
| End the row | console.log(line) |
| Spaced digits | line += j + " " |
| User input | parseInt(prompt(), 10) |
| Program 3 contrast | for (let i = rows; i >= 1; i--) with j = i..1 |
Same left-aligned descending triangle — different ways to control rows and formatting.
i = 0..rows-1Changes inner stop each line
j = rows..i+1Fixed start, descending digits
i = 0Longest row on top
range stopRemember range stop is exclusive
Reach for this pattern when teaching fixed-start inner loops and shrinking row lengths.
Natural follow-up — every row keeps the same starting digit while the stop point shrinks.
Outer/inner bound practice with an immediate visual check.
Combine loops with prompt() for a flexible row count.
Compare Program 3 (reverse descending) and Program 5 (ascending triangle) next.
This is a console teaching pattern — not how you build modern app screens.
Key benefit: one small program that locks in nested loops, output sequencing, and O(n²) thinking.
Choose a row count between 3 and 9 and draw the left-aligned descending triangle in the browser.
Three complete JavaScript programs — fixed rows, prompt() input, and spaced output variant. Click View Output to reveal sample console results, or Try it Yourself to run the code live.
Print five rows of the left-aligned descending triangle with nested loops.
rows = 5Hard-coded row count — ideal for first demos and screenshots.
const rows = 5;
for (let i = 0; i < rows; i++) {
let line = "";
for (let j = rows; j > i; j--) {
line += j;
}
console.log(line);
} When i = 0, the inner loop appends 5, 4, 3, 2, 1 — output 54321. When i = 4, only one digit appends — output 5. The outer loop increases i each row, shortening the inner loop.
Read the row count with prompt() instead of hard-coding 5.
Read rows with prompt() and parseInt() (check with Number.isFinite in real apps).
const rowsInput = prompt("Enter the number of rows:");
const rows = parseInt(rowsInput, 10);
if (!Number.isFinite(rows) || rows < 1) {
console.log("Please enter a positive integer.");
} else {
for (let i = 0; i < rows; i++) {
let line = "";
for (let j = rows; j > i; j--) {
line += j;
}
console.log(line);
}
} Same fixed-start inner-loop core as Example 1; only the source of rows changes from a literal to user input. Non-numeric input yields NaN with bare parseInt() — use Number.isFinite for safer labs.
Add a space between digits for easier reading on each row.
Keep rows = 5 but append each digit followed by a space.
const rows = 5;
for (let i = 0; i < rows; i++) {
let line = "";
for (let j = rows; j > i; j--) {
line += j + " ";
}
console.log(line);
} Only the append changes — line += j + " " instead of line += j. Loop bounds stay the same as Example 1.
No imports needed for fixed rows; use prompt() when reading. Set rows = 5 and loop variables i, j.
for (let i = 0; i < rows; i++) — ascending outer loop changes the inner stop each row.
for (let j = rows; j > i; j--) — always starts at rows and counts down.
console.log(line) ends the row after the inner loop finishes.
Each row starts at rows — O(n²) time, O(1) extra memory.
rows = 5Trace each outer-loop value of i, the inner-loop range, digit count, and full row output.
i | Inner loop (j) | Prints | Row output |
|---|---|---|---|
0 | 5, 4, 3, 2, 1 | 5 | 54321 |
1 | 5, 4, 3, 2 | 4 | 5432 |
2 | 5, 4, 3 | 3 | 543 |
3 | 5, 4 | 2 | 54 |
4 | 5 | 1 | 5 |
Prints per row = rows - i — total prints = n(n+1)/2 for n rows.
Where this tiny pattern (and its loop structure) shows up beyond the homework prompt.
Clearest visual proof that outer and inner bounds interact.
Example: flip j-- to j++ and watch digit order change.
Foundation for inverted, pyramid, diamond, and hollow variants.
Example: continue to Program 5 for an ascending number triangle.
Practice print vs row newline without complex math.
Example: put console.log(line) inside the inner loop by mistake.
Add spaces between digits once the two-loop structure works.
Example: use line += j + " " between digits on each row.
Triangular totals make O(n²) concrete for beginners.
Example: count printed digits for rows = 5 — total is 15 (5+4+3+2+1).
Pair the pattern with Number.isFinite and positive-row checks.
Example: reject rows <= 0 and re-prompt.
Pro Tip: when an interviewer asks for patterns, explain the outer/inner roles first — then write the loops. The story matters as much as the code.
Why this pattern earns a permanent spot in beginner JavaScript courses.
Wrong bounds show up immediately as a broken staircase.
Only loops and console output — no arrays or math libraries.
Invert, center, hollow, or change the fill character with small edits.
Streaming output needs no storage beyond loop counters.
Pro Tip: trace i and j on paper for rows = 3 before coding — watch how each row shortens by one digit.
Small habits that keep number-pattern code clean.
Outer loop uses i < rows; inner loop uses j > i — the stop value is not included.
Use Number.isFinite(rows) so bad input does not crash when converting rows.
Only call console.log(line) after the inner loop finishes the row.
for (let j = rows; j > i; j--) always begins at rows — only the stop changes.
Trace i = 0, 1, 2 on paper before coding the full rows = 5 demo.
Pro Tip: if the output is a vertical list of single digits per line, you almost certainly put console.log(line) inside the inner loop.
Mistakes that commonly break left-aligned descending number triangles.
Each digit lands on its own line — you get a column, not a triangle.
→ Use line += j for digits; console.log(line) only after the inner loop.
for (let j = rows; j >= 1; j--) on every row appends a full rectangle — the stop must change with i.
→ Keep for (let j = rows; j > i; j--) so each row shortens correctly.
for (let i = rows; i >= 1; i--) with j = i..1 gives Program 3’s shape, not this one.
→ Use for (let i = 0; i < rows; i++) with for (let j = rows; j > i; j--).
Omitting console.log(line) glues every digit onto one endless line.
→ Always end the row after the inner loop.
parseInt(prompt())Letters or empty input yield NaN with bare parseInt().
→ Catch ValueError and re-prompt on failure.
Check these inputs before calling the solution done.
Output is just the digit rows on one line.
Outer loop never runs — print nothing or show a message.
rows < 0Treat as invalid; re-prompt instead of silent empty output.
Two rows: 21 and 2.
Bare parseInt(prompt()) yields NaN on bad input — use Number.isFinite first.
Each row prints rows - i digits — total work grows as n(n+1)/2.
Try these variations to lock in the pattern.
line += j + " " between digitsi = 0..rows-1. Inner loop: j = rows..i+1 via for (let j = rows; j > i; j--).line += j builds the row; console.log(line) advances — call log only after the inner loop.rows > 0 for interactive programs; rows = 1 should print a single digit matching rows.rows — compare with Program 3 where the start digit shifts each row.Quick Takeaway: outer loop i = 0..rows-1, inner loop for (let j = rows; j > i; j--) with line += j, then console.log(line).
| Program | Time | Extra space |
|---|---|---|
| Nested loops (Examples 1–3) | O(n²) | O(1) |
| Smaller demo (Example 3) | O(n²) | O(1) |
The left-aligned descending number triangle is a compact nested-loop lesson: a fixed inner start at rows with a shrinking stop on each row. Master the fixed-rows version, then try user input and spaced output.
Practice the three examples above, then continue to Program 5 for the ascending number triangle.
Every row starts at rows — build with line += j and break with console.log(line).
for (let i = 0; i < rows; i++) in the outer loopfor (let j = rows; j > i; j--) always starts at rowsline += j for digits and console.log(line) after each rowrows ≥ 1 for interactive programsNumber.isFinite(rows) after parseInt(prompt())console.log(line) inside the inner digit loopfor (let i = rows; i >= 1; i--) for the outer loop (that is Program 3)j > i excludes the stop digitrows = 1 edge casePrint the pattern the beginner-friendly way.
Every row starts at rows
Definitionfor (let i = 0; i < rows; i++)
Codefor (let j = rows; j > i; j--)
CodeEnds each row
ShapeO(n²) time
AnalysisEach row starts at rows and counts down to a shrinking limit. Row i appends rows - i digits — total appends = n(n+1)/2; output is left-aligned with no leading spaces.
Move on to the ascending number triangle in the JavaScript number-pattern series.
12 people found this page helpful