Shape Rule
Shrinking rows
Row 1 prints five 1s, row 2 prints four 2s, row 5 prints a single 1.

The bidirectional number triangle prints 11111, 2222, 333, 22, 1 — a natural step after the centered pyramid in Program 24. This tutorial covers shrinking rows, if/else digit mapping, a live preview, algorithm steps, worked JavaScript examples, edge cases, and complexity.
Shrinking rows
Row 1 prints five 1s, row 2 prints four 2s, row 5 prints a single 1.
i = 1..rows
for (let i = 1; i <= rows; i++) walks each row top to bottom.
j = i..rows
for (let j = i; j <= rows; j++) prints fewer digits as i grows.
rows + 1 - i mirror
i < 4 prints i; else prints rows + 1 - i for rows 4 and 5.
3–9 rows
Pick a row count and draw the bidirectional triangle instantly in the browser.
Complexity
Total prints are triangular — scales as n² for n rows.
A bidirectional number triangle prints repeated digits per row with shrinking length — digits rise then mirror down. With rows = 5, the output is 11111, 2222, 333, 22, 1.
In JavaScript you use an outer loop for rows, a shrinking inner loop j = i..rows, and an if/else to pick which digit to repeat.
It combines shrinking inner loops with conditional mapping — a step up from Program 24’s spacing logic.
j = i..rows — each row prints fewer digits.
i < 4 repeats 1, 2, 3.
rows + 1 - i produces 2 and 1 on last rows.
Follow Program 24; continue to Program 26 (diagonal asterisk) next.
In short: for each i, repeat a digit (rows - i + 1) times — use i when i < rows - 1, else rows + 1 - i.
Given a positive integer rows (e.g. 5), print a shrinking triangle where each row repeats one digit — rising on early rows, mirroring down on the last rows.
# rows = 5 (conceptual shape)
# 11111
# 2222
# 333
# 22
# 1 | Item | Type | Description |
|---|---|---|
rows | int | Number of rows — outer loop runs from 1 to rows. |
i | int | Outer loop — current row number (also the digit for early rows). |
j | int | Inner loop — j = i..rows controls shrinking row length. |
val | int | Digit to repeat — i or rows + 1 - i via if/else. |
for i from 1 to rows:
val = i < rows - 1 ? i : rows + 1 - i
line = ""
for j from i to rows:
line += val
console.log(line) | Approach | Idea | Best for |
|---|---|---|
| If/else mapping | 11111, 2222, … 1 | Learning and interviews |
| User-input rows | const rows = parseInt(prompt(...), 10) | Flexible console programs |
| Ternary val | val = i if i < rows - 1 else rows + 1 - i | Compact generalized version |
| Goal | Pattern |
|---|---|
| Walk rows | for (let i = 1; i <= rows; i++) |
| Shrink inner loop | for (let j = i; j <= rows; j++) |
| Pick digit (fixed) | if (i < 4) { line += i; } else { line += rows + 1 - i; } |
| Pick digit (general) | val = i if i < rows - 1 else rows + 1 - i |
| End the row | console.log(line) |
| User input | const rows = parseInt(prompt(...), 10) |
Same bidirectional triangle — different ways to control rows and formatting.
i = 1..rowsOne row per outer iteration
j = i..rowsShrinking row length each row
rows + 1 - iMirrors digits on last rows
if/elseCompute val once per row, not per column
Reach for this pattern when teaching shrinking inner loops, conditional digit mapping, and bidirectional output.
Natural follow-up after Program 24 — introduces if/else mapping and shrinking rows.
Outer/inner bound practice with an immediate visual check.
Combine loops with prompt() for a flexible row count.
Compare Program 24 (centered pyramid) and Program 26 (diagonal asterisk) 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 bidirectional number triangle in the browser.
Three complete JavaScript programs — fixed rows, user 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 bidirectional triangle with if/else mapping.
rows = 5Hard-coded row count — ideal for first demos and screenshots.
const rows = 5;
for (let i = 1; i <= rows; i++) {
let line = "";
for (let j = i; j <= rows; j++) {
if (i < 4) {
line += i;
} else {
line += rows + 1 - i;
}
}
console.log(line);
} When i = 1, append five 1s — output 11111. When i = 3, append three 3s — output 333. When i = 4, the else branch appends rows + 1 - 4 = 2 twice — output 22. When i = 5, append rows + 1 - 5 = 1 once.
Read the row count with prompt() instead of hard-coding 5.
Read rows with prompt() and parseInt(); use a ternary expression to generalize the digit mapping.
const rowsInput = prompt("Enter rows:");
const rows = parseInt(rowsInput, 10);
for (let i = 1; i <= rows; i++) {
const val = i < rows - 1 ? i : rows + 1 - i;
let line = "";
for (let j = i; j <= rows; j++) {
line += val;
}
console.log(line);
} Same shrinking inner loop as Example 1; the ternary i < rows - 1 ? i : rows + 1 - i generalizes the if/else mapping for any row count. Non-numeric input yields NaN with bare parseInt(prompt(), 10) — validate with Number.isFinite for safer labs.
Add a space between repeated digits for easier reading.
Keep rows = 5 but append each digit followed by a space.
const rows = 5;
for (let i = 1; i <= rows; i++) {
const val = i < rows - 1 ? i : rows + 1 - i;
let line = "";
for (let j = i; j <= rows; j++) {
line += val + " ";
}
console.log(line);
} Only the append changes — line += val + " " instead of line += val. The shrinking loop and digit mapping stay the same.
console.log is built in; use prompt() when reading input. Set loop variables i, j and rows = 5.
for (let i = 1; i <= rows; i++) — one row per iteration.
for (let j = i; j <= rows; j++) — row length shrinks as i grows.
if (i < 4) appends i; else line += rows + 1 - i mirrors down.
console.log(line) ends the row after the inner loop.
Digits rise then mirror down — O(n²) time, O(1) extra memory.
rows = 5Trace each outer-loop value of i, the digit chosen, inner-loop count, and row output.
i | Digit (val) | Inner loop (j) | Prints | Row output |
|---|---|---|---|---|
1 | 1 (i < 4) | 1..5 (5 times) | 5 | 11111 |
2 | 2 | 2..5 (4 times) | 4 | 2222 |
3 | 3 | 3..5 (3 times) | 3 | 333 |
4 | 2 (rows + 1 - i) | 4..5 (2 times) | 2 | 22 |
5 | 1 (rows + 1 - i) | 5..5 (1 time) | 1 | 1 |
Prints per row = rows - i + 1 — 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: change j = i to j = 1 and watch rows stop shrinking.
Foundation for inverted, pyramid, diamond, and hollow variants.
Example: use rows + 1 - i for a fully symmetric variant.
Practice line += vs console.log(line) without complex math.
Example: put console.log(line) inside the inner loop by mistake.
Swap digits for letters or add spaces once the loop works.
Example: print val + " " for spaced repeated digits.
Triangular totals make O(n²) concrete for beginners.
Example: count printed numbers for rows = 5 → 5 + 4 + 3 + 2 + 1 = 15.
Pair the pattern with Number.isFinite and positive-row checks.
Example: reject max <= 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 the shrinking inner loop on paper for rows = 3 before coding — mapping bugs hide in the rows + 1 - i threshold.
Small habits that keep number-pattern code clean.
Do not reset val inside the inner loop — compute it once per row.
prompt()Validate parseInt(prompt(), 10) with Number.isFinite so bad input does not produce NaN.
console.log(line) OutsideOnly call console.log(line) after the inner loop finishes the row.
Write each i, digit chosen, and print count before coding.
Trace rows = 3 on paper before coding larger demos.
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 bidirectional number triangle patterns.
Each digit lands on its own line — you get a column, not a triangle.
→ Use line += val; console.log(line) only after the inner loop.
Putting the if/else inside the inner loop works but is wasteful — compute val once per row.
→ Set val before the inner loop, then just line += val.
Using j = 1 to rows prints full-width rows — no shrinking.
→ Use for (let j = i; j <= rows; j++) so each row is shorter.
Using i < rows instead of i < rows - 1 skips the mirror on the last row.
→ For generalized code use i if i < rows - 1 else rows + 1 - i.
Letters or empty input yield NaN from bare parseInt(prompt(), 10).
→ Validate with Number.isFinite and re-prompt on failure.
Check these inputs before calling the solution done.
Output is just 1 — one digit, one row.
Outer loop never runs — print nothing or show a message.
rows < 0Treat as invalid; re-prompt instead of silent empty output.
Two rows: 11 and 1.
parseInt(prompt(), 10) yields NaN — validate with Number.isFinite first.
Output grows as n² characters — fine for labs, noisy for huge n.
Try these variations to lock in the pattern.
* on diagonalPrintTriangle(int rows)Main with user inputrows + 1 - i for all rows, not just last twoj = i..rows — row length = rows - i + 1 digits per row.line += val repeats the digit; console.log(line) advances — mix them carefully.rows > 0 for interactive programs; rows = 1 prints a single 1.val once per row outside the inner loop — cleaner and slightly faster.Quick Takeaway: outer loop i = 1..rows, shrinking inner loop j = i..rows, if/else digit mapping, then console.log(line) after each row.
| Program | Time | Extra space |
|---|---|---|
| Nested loops (Examples 1–2) | O(n²) | O(1) |
| Spaced output (Example 3) | O(n²) | O(1) |
The bidirectional number triangle is a compact lesson in shrinking loops and conditional mapping: repeat a digit per row with j = i..rows, then mirror down with rows + 1 - i. Master the fixed-rows version, then try user input and spaced output.
Practice the three examples above, then continue to Program 26 for the descending pattern with diagonal asterisk.
Compute val once per row — validate rows when reading from the console.
for (let i = 1; i <= rows; i++) in the outer loopfor (let j = i; j <= rows; j++)val once per row before the inner loopparseInt(prompt(), 10) with Number.isFinite before using rowsconsole.log(line) inside the inner loopj = 1 in the inner loop — rows won’t shrinkrows + 1 - i in generalized code — use rows + 1 - irows = 1 edge casePrint the pattern the beginner-friendly way.
Shrink + repeat
DefinitionShrinking rows
Coderows + 1 - i mirror
Code1,2,3 then 2,1
ShapeO(n²) time
AnalysisThis pattern prints repeated digits per row. The inner loop runs from j = i to rows, shrinking each row. The row digit is i for the first half, then switches to rows + 1 - i to produce 22 and 1.
Move on to the descending number pattern with diagonal asterisk in the JavaScript number-pattern series.
12 people found this page helpful