Using Python To Merge Array of Ranges

Published: (December 30, 2025 at 05:16 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

Counting Unique Numbers Across Overlapping Ranges

Inspired by an Advent of Code 2025 challenge and a previous booking‑system implementation.

The Naïve Approach

unique_values = set()
for start, end in ranges:
    unique_values.update(range(start, end + 1))

For a range like 1‑100_000_000_000 this would create 1 billion integers in memory – far too expensive. We need a way to count the numbers without materialising every single value.

Solution – Merge First, Count Later

Instead of storing every number, we merge overlapping (or adjacent) ranges into a smaller set of disjoint intervals and then compute the total count mathematically.

Example Ranges

3‑5,
10‑14,
16‑20,
12‑18

10‑14 and 12‑18 overlap (they share 12, 13, 14). Merging them yields 10‑18. Without merging we would double‑count the overlapping part or waste memory.

How to Merge Overlapping Ranges

1️⃣ Sort the Ranges

Sorting by the start position lets us process the intervals from left‑to‑right on the number line.

sorted_ranges = sorted(fresh_ranges)
# Before: [(3,5), (10,14), (16,20), (12,18)]
# After : [(3,5), (10,14), (12,18), (16,20)]

When the list is sorted, any new range can only overlap with the last merged range, so we never need to look back further.

Sorted number line view:
   3---5      10----14
              12------18
                  16----20
# Overlaps flow left‑to‑right!

2️⃣ Merge Overlapping / Adjacent Ranges

merged = []
for start, end in sorted_ranges:
    # If the current range touches or overlaps the last merged range …
    if merged and start <= merged[-1][1] + 1:
        # … extend the last merged interval
        merged[-1] = (merged[-1][0], max(merged[-1][1], end))
    else:
        # No overlap – start a new interval
        merged.append((start, end))

Why start <= merged[-1][1] + 1?

SituationLast mergedCurrentCheck (start <= last_end + 1)Result
Adjacent (touching)(10, 14)(15, 20)15 ≤ 14 + 1 → TrueMerge
Gap(3, 5)(10, 14)10 ≤ 5 + 1 → FalseSeparate

The + 1 allows adjacent intervals (e.g., (5, 9) and (10, 15)) to be merged, because there is no integer between 9 and 10.

Visual illustration

Range A: (5, 9)   → 5,6,7,8,9
Range B: (10,15)  → 10,11,12,13,14,15
# They touch at 9→10
Check: 10 <= 9 + 1 → True → merge → (5,15)

Range A: (5, 9)   → 5,6,7,8,9
Range B: (11,15) → 11,12,13,14,15
# Gap at 10
Check: 11 <= 9 + 1 → False → keep separate

3️⃣ Count the Numbers

After merging, the total number of distinct integers is simply the sum of the lengths of the disjoint intervals.

total_count = sum(end - start + 1 for start, end in merged)
return total_count

Final Implementation

def count_unique_numbers(fresh_ranges):
    # 1️⃣ Sort by start position
    sorted_ranges = sorted(fresh_ranges)

    # 2️⃣ Merge overlapping / adjacent ranges
    merged = []
    for start, end in sorted_ranges:
        if merged and start <= merged[-1][1] + 1:
            # Overlap or adjacency – extend the last interval
            merged[-1] = (merged[-1][0], max(merged[-1][1], end))
        else:
            # No overlap – start a new interval
            merged.append((start, end))

    # 3️⃣ Compute total count
    total_count = sum(end - start + 1 for start, end in merged)
    return total_count

Real‑Life Use Cases

Appointment Booking Systems

Meeting A: 09:00‑10:30
Meeting B: 10:15‑11:00
Meeting C: 14:00‑15:30
Meeting D: 15:30‑17:00

Sorted: [(09:00,10:30), (10:15,11:00), (14:00,15:30), (15:30,17:00)]
Merged: [(09:00,11:00), (14:00,17:00)]

The merged intervals give the total occupied time without double‑counting overlapping meetings.

Other Scenarios

  • IP address allocation – merging CIDR blocks.
  • Genome interval analysis – combining overlapping gene regions.
  • Log file processing – consolidating time windows of activity.
  • Resource reservation – aggregating booked slots for equipment or rooms.

Overlap — Meetings C + D Touch (Adjacent)

Free times:

  • Before 9:00
  • 11:00 – 14:00
  • After 17:00

When is the room actually free?

IP Address Blocking

Block: 192.168.1.0 - 192.168.1.50
Block: 192.168.1.45 - 192.168.1.100
Block: 192.168.1.200 - 192.168.1.255

Without merging: check 3 rules for every connection.

With merging:

[(192.168.1.0, 192.168.1.100), (192.168.1.200, 192.168.1.255)]

Only 2 rules to check → faster firewall!

Maintenance Downtime

Server or machine scheduled for maintenance:

Maintenance A: Hour 0‑5
Maintenance B: Hour 4‑8
Maintenance C: Hour 20‑24

Merged downtime:

[(0, 8), (20, 24)]

Total downtime: 8 + 4 = 12 hours (not 17 hours if counted individually).

Why This Matters in All Cases

  • Avoid double‑counting overlapping periods.
  • Optimise checks – fewer ranges to search.
  • Find gaps – available time slots.
  • Accurate reporting – real busy vs. free time.
  • Performance – checking fewer merged ranges vs. many overlapping ones.

Conclusion

The above pattern/method is used by multiple companies worldwide for handling calendar invites, database indexing, network‑traffic analysis, and more.

Go ahead—use the code to boost your application’s performance.

If you’d like to chat or reach out, feel free to comment below or drop me a follow on X/Twitter.

Back to Blog

Related posts

Read more »