Interval Notation: Half-Open vs Closed
What are Half-Open Intervals?
Notation: [start, end) where:
startis included (closed bracket[)endis excluded (open parenthesis))
Visual Examples
Example 1: Simple Range
GridRange: { startRowIndex: 0, endRowIndex: 5 }
Means: [0, 5) = rows 0, 1, 2, 3, 4
Visual:
Row Included?
0 โ
YES (>= start)
1 โ
YES
2 โ
YES
3 โ
YES
4 โ
YES
5 โ NO (>= end, excluded!)
6 โ NO
Common mistake: Thinking row 5 is included!
Example 2: Single Row
GridRange: { startRowIndex: 3, endRowIndex: 4 }
Means: [3, 4) = row 3 ONLY
Visual:
Row Included?
2 โ NO
3 โ
YES (only this one!)
4 โ NO (excluded)
5 โ NO
Example 3: Empty Range
GridRange: { startRowIndex: 5, endRowIndex: 5 }
Means: [5, 5) = EMPTY (zero rows!)
This is VALID! Empty ranges are allowed.
Visual:
Row Included?
4 โ NO
5 โ NO (start == end โ empty!)
6 โ NO
Why Half-Open Intervals?
1. Adjacent Ranges Don't Overlap
โ
Half-open:
Range A: [0, 5) = rows 0-4
Range B: [5, 10) = rows 5-9
NO GAP, NO OVERLAP!
โ Closed intervals:
Range A: [0, 4] = rows 0-4
Range B: [5, 9] = rows 5-9
GAP at row 4.5? NO!
OR:
Range A: [0, 5] = rows 0-5
Range B: [5, 10] = rows 5-10
OVERLAP at row 5! Ambiguous!
2. Range Length is Simple
โ
Half-open: length = end - start
[0, 5) has length 5 - 0 = 5 โ
โ Closed: length = end - start + 1
[0, 4] has length 4 - 0 + 1 = 5 (extra +1 is error-prone!)
3. Matches Programming Conventions
Array slicing: arr[0:5] means indices 0-4
Python range: range(0, 5) yields 0,1,2,3,4
Google Sheets: GridRange uses half-open
Our library: Matches the API!
Common Mistakes & How to Avoid
Mistake 1: Including the End
โ WRONG:
"I want rows 0 through 5"
โ endRowIndex: 5
Result: Gets rows 0-4 (missing row 5!)
โ
CORRECT:
"I want rows 0 through 5"
โ endRowIndex: 6 (one more than you want!)
Result: [0, 6) = rows 0-5 โ
Mistake 2: Off-by-One on Single Cell
โ WRONG:
"I want cell A1 (row 0, col 0)"
โ { startRowIndex: 0, endRowIndex: 0 }
Result: [0, 0) = EMPTY!
โ
CORRECT:
"I want cell A1"
โ { startRowIndex: 0, endRowIndex: 1 }
Result: [0, 1) = row 0 only โ
Mistake 3: Assuming Inclusive
โ WRONG thinking:
endRowIndex: 5 means "up to and including row 5"
Result: Confusion when row 5 isn't included!
โ
CORRECT thinking:
endRowIndex: 5 means "up to but NOT including row 5"
= "stop BEFORE row 5"
= "last row is 4"
Mnemonic: "end is where you STOP, not where you INCLUDE"
2D Example: Full Grid Range
GridRange:
{
startRowIndex: 2,
endRowIndex: 5,
startColumnIndex: 1,
endColumnIndex: 4
}
Means: [2, 5) ร [1, 4)
= rows 2,3,4 ร columns 1,2,3
Visual Grid:
col 0 col 1 col 2 col 3 col 4
row 0 ยท ยท ยท ยท ยท
row 1 ยท ยท ยท ยท ยท
row 2 ยท โ
โ
โ
ยท
row 3 ยท โ
โ
โ
ยท
row 4 ยท โ
โ
โ
ยท
row 5 ยท ยท ยท ยท ยท
Covers 3 rows ร 3 columns = 9 cells
Converting Between Notations
Half-Open โ Inclusive (Internal Storage)
// GridRange uses [start, end)
const gridRange = { startRowIndex: 0, endRowIndex: 5 };
// Convert to inclusive [min, max] for internal math
const inclusive = [
gridRange.startRowIndex, // 0 (same)
gridRange.endRowIndex - 1 // 5-1 = 4 (subtract 1!)
];
Result: [0, 4] in inclusive notation
Inclusive โ Half-Open (API Return)
// Internal: [0, 4] inclusive
const inclusive = [0, 4];
// Convert to GridRange [start, end)
const gridRange = {
startRowIndex: inclusive[0], // 0 (same)
endRowIndex: inclusive[1] + 1 // 4+1 = 5 (add 1!)
};
Result: [0, 5) in half-open notation
Quick Reference
| Want | GridRange Setting | Result |
|---|---|---|
| Single cell (0,0) | start: 0, end: 1 | [0, 1) |
| Row 5 only | startRow: 5, endRow: 6 | [5, 6) |
| Rows 0-9 (10 rows) | startRow: 0, endRow: 10 | [0, 10) |
| Entire column A (0) | startCol: 0, endCol: 1 | [0, 1) |
| Empty range | startRow: 5, endRow: 5 | [5, 5) |
| Adjacent ranges | [0,5) then [5,10) | No gap! |
Remember: end is always "one past" what you want!
See Also:
- theoretical-foundation.md - Formal mathematical model
- Google Apps Script
GridRangeAPI documentation