Bidirectional Number Triangle in JavaScript

Beginner
⏱️ 8 min read
📚 Updated: Sep 2026
🎯 3 Code Examples
🚀 Live Preview
If/Else Mapping

What You’ll Learn

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.

Shape Rule

Shrinking rows

Row 1 prints five 1s, row 2 prints four 2s, row 5 prints a single 1.

Outer Loop

i = 1..rows

for (let i = 1; i <= rows; i++) walks each row top to bottom.

Shrinking Inner Loop

j = i..rows

for (let j = i; j <= rows; j++) prints fewer digits as i grows.

If/Else Mapping

rows + 1 - i mirror

i < 4 prints i; else prints rows + 1 - i for rows 4 and 5.

Live Preview

3–9 rows

Pick a row count and draw the bidirectional triangle instantly in the browser.

O(n²)

Complexity

Total prints are triangular — scales as for n rows.

Introduction

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.

Why it matters?

It combines shrinking inner loops with conditional mapping — a step up from Program 24’s spacing logic.

Key Highlights

Shrinking rows

j = i..rows — each row prints fewer digits.

Rising digits

i < 4 repeats 1, 2, 3.

Mirror down

rows + 1 - i produces 2 and 1 on last rows.

Series Foundation

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.

📝 Problem & Approach

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.

JavaScript
# rows = 5 (conceptual shape)
# 11111
# 2222
# 333
# 22
# 1

Inputs & Outputs

ItemTypeDescription
rowsintNumber of rows — outer loop runs from 1 to rows.
iintOuter loop — current row number (also the digit for early rows).
jintInner loop — j = i..rows controls shrinking row length.
valintDigit to repeat — i or rows + 1 - i via if/else.

Minimal workflow

Pseudocode
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 comparison

ApproachIdeaBest for
If/else mapping11111, 2222, … 1Learning and interviews
User-input rowsconst rows = parseInt(prompt(...), 10)Flexible console programs
Ternary valval = i if i < rows - 1 else rows + 1 - iCompact generalized version

⚡ Quick Reference

GoalPattern
Walk rowsfor (let i = 1; i <= rows; i++)
Shrink inner loopfor (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 rowconsole.log(line)
User inputconst rows = parseInt(prompt(...), 10)

📋 Fixed rows vs User Input vs Spaced Output

Same bidirectional triangle — different ways to control rows and formatting.

Outer loop
i = 1..rows

One row per outer iteration

Inner loop
j = i..rows

Shrinking row length each row

Mapping
rows + 1 - i

Mirrors digits on last rows

Learning tip
if/else

Compute val once per row, not per column

Context

When This Pattern Shows Up

Reach for this pattern when teaching shrinking inner loops, conditional digit mapping, and bidirectional output.

  1. Post pyramid exercise

    Natural follow-up after Program 24 — introduces if/else mapping and shrinking rows.

  2. Nested-loop warm-up

    Outer/inner bound practice with an immediate visual check.

  3. Console I/O practice

    Combine loops with prompt() for a flexible row count.

  4. Gateway to variants

    Compare Program 24 (centered pyramid) and Program 26 (diagonal asterisk) next.

  5. Not a UI layout tool

    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.

🔮 Live Preview

Choose a row count between 3 and 9 and draw the bidirectional number triangle in the browser.

Try 4, 5, or 7. Max up to 9 in this preview.

Live result
Press "Draw pattern".

Examples Gallery

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.

📚 Getting Started

Print five rows of the bidirectional triangle with if/else mapping.

Example 1 — Fixed rows = 5

Hard-coded row count — ideal for first demos and screenshots.

JavaScript
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);
}
Try it Yourself

How It Works

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.

📈 User Input

Read the row count with prompt() instead of hard-coding 5.

Example 2 — User Input

Read rows with prompt() and parseInt(); use a ternary expression to generalize the digit mapping.

JavaScript
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);
}
Try it Yourself

How It Works

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.

⚡ Spaced Output

Add a space between repeated digits for easier reading.

Example 3 — Spaced Digits

Keep rows = 5 but append each digit followed by a space.

JavaScript
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);
}
Try it Yourself

How It Works

Only the append changes — line += val + " " instead of line += val. The shrinking loop and digit mapping stay the same.

🧠 How the Algorithm Prints Rows

1

Set up

console.log is built in; use prompt() when reading input. Set loop variables i, j and rows = 5.

Setup
2

Outer loop walks rows

for (let i = 1; i <= rows; i++) — one row per iteration.

Row
3

Shrinking inner loop (j)

for (let j = i; j <= rows; j++) — row length shrinks as i grows.

Shrink
4

If/else digit mapping

if (i < 4) appends i; else line += rows + 1 - i mirrors down.

Mapping
5

New line

console.log(line) ends the row after the inner loop.

Break
=

Bidirectional triangle complete

Digits rise then mirror down — O(n²) time, O(1) extra memory.

🔎 Worked Walkthrough — rows = 5

Trace each outer-loop value of i, the digit chosen, inner-loop count, and row output.

iDigit (val)Inner loop (j)PrintsRow output
11 (i < 4)1..5 (5 times)511111
222..5 (4 times)42222
333..5 (3 times)3333
42 (rows + 1 - i)4..5 (2 times)222
51 (rows + 1 - i)5..5 (1 time)11

Prints per row = rows - i + 1 — total prints = n(n+1)/2 for n rows.

Use Cases

Where this tiny pattern (and its loop structure) shows up beyond the homework prompt.

1. Teaching Nested Loops

Clearest visual proof that outer and inner bounds interact.

Example: change j = i to j = 1 and watch rows stop shrinking.

2. Pattern Series Base

Foundation for inverted, pyramid, diamond, and hollow variants.

Example: use rows + 1 - i for a fully symmetric variant.

3. Console Formatting Drills

Practice line += vs console.log(line) without complex math.

Example: put console.log(line) inside the inner loop by mistake.

4. Character Substitution

Swap digits for letters or add spaces once the loop works.

Example: print val + " " for spaced repeated digits.

5. Complexity Intuition

Triangular totals make O(n²) concrete for beginners.

Example: count printed numbers for rows = 5 → 5 + 4 + 3 + 2 + 1 = 15.

6. Input Validation Labs

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.

Advantages

Why this pattern earns a permanent spot in beginner JavaScript courses.

  1. 1. Instant Visual Feedback

    Wrong bounds show up immediately as a broken staircase.

  2. 2. Minimal Concepts

    Only loops and console output — no arrays or math libraries.

  3. 3. Easy to Extend

    Invert, center, hollow, or change the fill character with small edits.

  4. 4. O(1) Extra Memory

    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.

Usage Tips

Small habits that keep number-pattern code clean.

  1. 1. Name Bounds Clearly

    Do not reset val inside the inner loop — compute it once per row.

  2. 2. Validate prompt()

    Validate parseInt(prompt(), 10) with Number.isFinite so bad input does not produce NaN.

  3. 3. Keep console.log(line) Outside

    Only call console.log(line) after the inner loop finishes the row.

  4. 4. Trace i and val on Paper

    Write each i, digit chosen, and print count before coding.

  5. 5. Dry-Run One Small n

    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.

Common Pitfalls

Mistakes that commonly break bidirectional number triangle patterns.

  1. 1. Newline Inside the Inner Loop

    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.

  2. 2. Recomputing val Inside 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.

  3. 3. Wrong Inner Loop Bound

    Using j = 1 to rows prints full-width rows — no shrinking.

    → Use for (let j = i; j <= rows; j++) so each row is shorter.

  4. 4. Wrong Mirror Threshold

    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.

  5. 5. Unchecked input

    Letters or empty input yield NaN from bare parseInt(prompt(), 10).

    → Validate with Number.isFinite and re-prompt on failure.

Edge Cases

Check these inputs before calling the solution done.

rows = 1

Single row

Output is just 1 — one digit, one row.

rows = 0

Empty pattern

Outer loop never runs — print nothing or show a message.

Negative

rows < 0

Treat as invalid; re-prompt instead of silent empty output.

rows = 2

Smallest triangle

Two rows: 11 and 1.

Bad input

Non-numeric input

parseInt(prompt(), 10) yields NaN — validate with Number.isFinite first.

Large rows

Large row count

Output grows as n² characters — fine for labs, noisy for huge n.

🎯 Practice Problems

Try these variations to lock in the pattern.

1. Centered pyramid

2. Diagonal asterisk

  • Descending numbers with * on diagonal
  • Continue with Program 26

3. Extract a method

  • Move pattern logic into PrintTriangle(int rows)
  • Call from Main with user input

4. Full mirror mapping

  • Use rows + 1 - i for all rows, not just last two
  • Compare symmetric vs bidirectional output

Notes

  • Shrinking rows. Inner loop j = i..rows — row length = rows - i + 1 digits per row.
  • line += val repeats the digit; console.log(line) advances — mix them carefully.
  • Validate rows > 0 for interactive programs; rows = 1 prints a single 1.
  • Compute 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.

⏱️ Time and Space Complexity

ProgramTimeExtra space
Nested loops (Examples 1–2)O(n²)O(1)
Spaced output (Example 3)O(n²)O(1)
Wrap Up

🎉 Conclusion

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.

💡 Best Practices

✅ Do

  • Use for (let i = 1; i <= rows; i++) in the outer loop
  • Shrink with for (let j = i; j <= rows; j++)
  • Compute val once per row before the inner loop
  • Validate parseInt(prompt(), 10) with Number.isFinite before using rows
  • State O(n²) time when asked about complexity

❌ Don’t

  • Call console.log(line) inside the inner loop
  • Use j = 1 in the inner loop — rows won’t shrink
  • Hard-code rows + 1 - i in generalized code — use rows + 1 - i
  • Recompute the digit mapping on every inner iteration
  • Ignore bad console input in user-facing demos
  • Skip the rows = 1 edge case

Key Takeaways

Knowledge Unlocked

Five things to remember about this triangle pattern

Print the pattern the beginner-friendly way.

5
Core concepts
02

j = i..rows

Shrinking rows

Code
+ 03

if/else

rows + 1 - i mirror

Code
04

Bidirectional

1,2,3 then 2,1

Shape
O 05

Complexity

O(n²) time

Analysis

❓ Frequently Asked Questions

For i = 5, the condition i < 4 is false, so the program appends rows + 1 - i which becomes 1.
Because the inner loop runs from j = i to rows. As i increases, the inner loop executes fewer times.
Digits rise (1, 2, 3) on early rows then mirror down (2, 1) on the last rows via the rows + 1 - i mapping.
line += val repeats the digit on the same line. console.log(line) ends the row after the inner loop finishes.
A single inner loop with a digit mapping keeps the shrinking row logic in one place.
Replace 5 with rows and use val = i < rows - 1 ? i : rows + 1 - i — see Example 2.
O(n²) for n rows because total prints are triangular (n + (n-1) + … + 1).
Use parseInt with Number.isFinite or validate the raw string before converting so bad input does not produce NaN.
Only one row prints — a single 1.

Did you Know? 🔊

This 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.

Continue to Program 26

Move on to the descending number pattern with diagonal asterisk in the JavaScript number-pattern series.

Program 26 tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

12 people found this page helpful