Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

prevOpen Source→ranges-pushnext

ranges-push7.3.1

Gather string index ranges

Downloads per monthChangelogMIT Licenselibera manifesto
  • the top
  • Installation
  • Quick Take
  • Examples
  • API
  • API — DEFAULTS
  • API — VERSION
  • API — TYPES
  • OPTS — MERGETYPE
  • ADD()
  • CURRENT()
  • FIRSTCOVERS()
  • WIPE()
  • REPLACE()
  • LAST()
  • IN OUR CASE
  • Changelog

No 3rd party dependencies. All dependencies and devDependencies, checked recursively, are Codsen packages.

Permalink to InstallationInstallation

Permalink to Quick TakeQuick Take

Permalink to ExamplesExamples

  • Add a complete array of ranges in one call
  • Read the most recently gathered range
  • Collapse replacement whitespace to one character
  • Retain at most two line breaks in replacement whitespace
  • Concatenate replacement values when same-start ranges merge
  • Keep the later replacement value when same-start ranges merge
  • Replace all previously gathered ranges
  • Accept the string form of merge type one
  • Accept the string form of merge type two
  • Clear every gathered range

API

The package exports the Ranges class. Create an instance, add ranges, then call current() when you need the sorted and merged result:

import { Ranges } from "ranges-push";

const ranges = new Ranges();
ranges.add(6, 10);
ranges.add(15, 20, "bbb");
ranges.add(10, 15, "aaa");

console.log(ranges.current());
// => [[6, 20, "aaabbb"]]

You can pass an options object to the constructor:

const ranges = new Ranges({
  limitToBeAddedWhitespace: true,
  limitLinebreaksCount: 2,
  mergeType: 2,
});
KeyTypeDefaultDescription
limitToBeAddedWhitespace
Type: Boolean
Default: false
limitToBeAddedWhitespaceBooleanfalseCollapses whitespace at the edges of insertion strings. Whitespace-only strings become either one space or a limited run of line breaks.
limitLinebreaksCount
Type: Number
Default: 1
limitLinebreaksCountNumber1Sets the maximum consecutive line breaks retained when whitespace limiting is enabled. Use 2 to allow one blank line.
mergeType
Type: 1, 2, "1", or "2"
Default: 1
mergeType1, 2, "1", or "2"1Controls how insertion values are combined. String aliases are normalized to numbers. See mergeType.

limitToBeAddedWhitespace must be a Boolean. limitLinebreaksCount must be a natural number or zero. The resolved options in ranges.opts are normalized and read-only.

Here are all defaults in one place for copying:

The instance provides these methods:

  • add() and push()
  • current()
  • firstCovers()
  • wipe()
  • replace()
  • last()

API — defaults

You can import defaults:

It's a plain object:

The main function calculates the options to be used by merging the options you passed with these defaults.

API — version

You can import version:

API — types

The package is written in TypeScript and exports these public types:

TypeDescription
Opts
Type: Opts
OptsConstructor options.
Range<InsertValue>
Type: Range<InsertValue>
Range<InsertValue>A normalized two- or three-element range with numeric indexes.
RangeInput<InsertValue>
Type: RangeInput<InsertValue>
RangeInput<InsertValue>An input range whose indexes can be numbers or digit-only numeric strings.
import type { Opts, Range, RangeInput } from "ranges-push";

Ranges is generic. Its insertion value defaults to string | number | null | undefined, and callers can narrow that type:

const ranges = new Ranges<string | null>();
const result: Range<string | null>[] | null = ranges.current();

opts.mergeType

Overlapping and touching ranges are sorted and merged. When multiple ranges contribute a third, insertion value, mergeType decides how clashes are resolved.

const first = [1, 2, "a"];
const second = [1, 2, "b"];
  • With mergeType: 1, contributing values are combined. Strings concatenate and numbers use JavaScript’s + behavior. The example produces "ab".
  • With mergeType: 2, the newer value replaces the earlier value only when both ranges start at the same index. The example produces "b". Values from ranges with different starting indexes are still combined.

In either mode, a contributing null makes the merged insertion value null. An omitted value does not replace an existing value.

Choose the mode when you create the instance:

const ranges = new Ranges({ mergeType: 2 });

add()

Alias: push().

InputTypeRequiredDescription
deleteFrom
Type: Natural number or digit-only numeric string
deleteFromNatural number or digit-only numeric stringYesStart index, inclusive.
deleteTo
Type: Natural number or digit-only numeric string
deleteToNatural number or digit-only numeric stringYesEnd index, exclusive. It must be greater than or equal to deleteFrom.
addValue
Type: String, number, null, or undefined
addValueString, number, null, or undefinedNoValue to insert after deleting the slice.

Use equal start and end indexes for an insertion that deletes nothing:

ranges.add(2, 2, "insert here");

You can also pass one range tuple or a batch of ranges:

ranges.add([1, 3, "a"]);
ranges.add([
  [5, 7],
  [9, 9, 123],
]);

Each call is validated before the instance changes. Malformed tuples, reversed indexes, unsafe coercions, mixed batches, and a fourth scalar argument throw a package-tagged TypeError. An empty array or a nullish whole input is a no-op.

When a new range starts exactly where the last range ends, add() merges it immediately. Other ranges remain in insertion order until current() canonicalizes them. Immediate and deferred merging use the same insertion-value rules.

current()

Returns the current ranges after sorting and merging all touching or overlapping ranges. It returns null when there is no effective range.

const ranges = new Ranges();
ranges.add([4, 5]);
ranges.add([1, 2]);

console.log(ranges.current());
// => [[1, 2], [4, 5]]
const ranges = new Ranges();
ranges.add([
  [2, 5],
  [2, 3],
  [1, 10],
]);

console.log(ranges.current());
// => [[1, 10]]

Canonicalization updates the instance’s internal range state. Repeated calls reuse the cached result while neither the accumulated ranges nor the previous result has changed. Changes made through the public ranges array are detected before a cached value is reused, but application code should prefer the class methods instead of mutating that array directly.

Finish collecting ranges before applying them to the source string. Merging intentionally removes boundaries between ranges, so later additions cannot recover the original separation.

firstCovers()

firstCovers(index) reports whether the first canonical range starts at zero and reaches the supplied natural-number index. It handles both sorted and unsorted accumulated ranges without changing their order.

const ranges = new Ranges();
ranges.add([
  [4, 8],
  [0, 5],
]);

console.log(ranges.firstCovers(7));
// => true

wipe()

Clears all accumulated ranges. current() and last() then return null, and you can start adding ranges again.

ranges.wipe();

replace()

Replaces the complete accumulated set with a validated batch. The class copies each tuple, so later changes to the caller’s array do not change the stored ranges. Passing null, undefined, or an empty array clears the instance.

const ranges = new Ranges();
ranges.replace([
  [1, 2, "a"],
  [3, 4, "b"],
  [9, 10],
]);

console.log(ranges.current());
// => [[1, 2, "a"], [3, 4, "b"], [9, 10]]

last()

Returns the last currently stored range, or null when the instance is empty. It does not sort or merge the accumulated ranges.

console.log(ranges.last());
// => [9, 10]

Tip: Pass the result of current() to ranges-apply to delete and replace all recorded slices in a string.

In our case

This library began as part of email-comb. It became a separate package when html-img-alt needed the same range accumulator. Detergent and other Codsen packages now use it too.

Permalink to changelogChangelog

Open Changelog
↑ back to top
prev next

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab