range-ts
Version:
RangeMap implementation based on Guava
332 lines • 17.3 kB
JavaScript
import { __read, __spreadArray } from "tslib";
import { BoundType } from "../core/bound-type";
import { NumberRange } from "../number-range/number-range";
import { RangeMap } from "./range-map";
import { isEqual } from "lodash-es";
describe("RangeMap", function () {
describe("put", function () {
it("should handle non-overlapping ranges and values", function () {
var rangeMap = new RangeMap();
rangeMap.put(NumberRange.closedOpen(1, 3), "a");
rangeMap.put(NumberRange.closedOpen(4, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(2);
expect(result[0][0].toString()).toBe("[1..3)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[4..6)");
expect(result[1][1]).toBe("b");
});
it("should handle overlapping ranges and values", function () {
var rangeMap = new RangeMap();
rangeMap.put(NumberRange.closedOpen(1, 3), "a");
rangeMap.put(NumberRange.closedOpen(2, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(2);
expect(result[0][0].toString()).toBe("[1..2)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[2..6)");
expect(result[1][1]).toBe("b");
});
it("should handle enclosed ranges", function () {
var rangeMap = new RangeMap();
rangeMap.put(NumberRange.closedOpen(1, 10), "a");
rangeMap.put(NumberRange.closedOpen(3, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(3);
expect(result[0][0].toString()).toBe("[1..3)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[3..6)");
expect(result[1][1]).toBe("b");
expect(result[2][0].toString()).toBe("[6..10)");
expect(result[2][1]).toBe("a");
});
it("should handle complex behavior", function () {
var rangeMap = new RangeMap();
rangeMap.put(NumberRange.upTo(8, BoundType.CLOSED), "a");
rangeMap.put(NumberRange.closedOpen(3, 6), "b");
rangeMap.put(NumberRange.closedOpen(8, 12), "c");
rangeMap.put(NumberRange.atLeast(18), "e");
expect(rangeMap.get(2)).toBe("a");
expect(rangeMap.get(3)).toBe("b");
expect(rangeMap.get(5)).toBe("b");
expect(rangeMap.get(6)).toBe("a");
expect(rangeMap.get(8)).toBe("c");
expect(rangeMap.get(13)).toBe(null);
expect(rangeMap.get(18)).toBe("e");
});
});
describe("put coalescing", function () {
describe("with different values", function () {
it("should handle non-overlapping ranges and values", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(1, 3), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(4, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(2);
expect(result[0][0].toString()).toBe("[1..3)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[4..6)");
expect(result[1][1]).toBe("b");
});
it("should handle overlapping ranges and values", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(1, 3), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(2, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(2);
expect(result[0][0].toString()).toBe("[1..2)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[2..6)");
expect(result[1][1]).toBe("b");
});
it("should handle enclosed ranges", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(1, 10), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(3, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(3);
expect(result[0][0].toString()).toBe("[1..3)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[3..6)");
expect(result[1][1]).toBe("b");
expect(result[2][0].toString()).toBe("[6..10)");
expect(result[2][1]).toBe("a");
});
it("should handle enclosing ranges", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(3, 6), "b");
rangeMap.putCoalescing(NumberRange.closedOpen(1, 10), "a");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(1);
expect(result[0][0].toString()).toBe("[1..10)");
expect(result[0][1]).toBe("a");
});
it("should handle complex behavior", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.upTo(8, BoundType.CLOSED), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(3, 6), "b");
rangeMap.putCoalescing(NumberRange.closedOpen(8, 12), "c");
rangeMap.putCoalescing(NumberRange.atLeast(18), "e");
expect(rangeMap.get(2)).toBe("a");
expect(rangeMap.get(3)).toBe("b");
expect(rangeMap.get(5)).toBe("b");
expect(rangeMap.get(6)).toBe("a");
expect(rangeMap.get(8)).toBe("c");
expect(rangeMap.get(13)).toBe(null);
expect(rangeMap.get(18)).toBe("e");
});
});
describe("with same values", function () {
it("should not combine non-connecting ranges", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(1, 3), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(4, 6), "a");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(2);
expect(result[0][0].toString()).toBe("[1..3)");
expect(result[0][1]).toBe("a");
expect(result[1][0].toString()).toBe("[4..6)");
expect(result[1][1]).toBe("a");
});
it("should combine overlapping ranges and values", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(1, 3), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(2, 6), "a");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(1);
expect(result[0][0].toString()).toBe("[1..6)");
expect(result[0][1]).toBe("a");
});
it("should handle enclosed ranges", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(1, 10), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(3, 6), "a");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(1);
expect(result[0][0].toString()).toBe("[1..10)");
expect(result[0][1]).toBe("a");
});
it("should filter out empty ranges", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(0, 24), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(0, 6), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(2);
expect(result[0][0].toString()).toBe("[0..6)");
expect(result[0][1]).toBe("b");
expect(result[1][0].toString()).toBe("[6..24)");
expect(result[1][1]).toBe("a");
});
it('should replace existing values', function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(0, 24), "a");
rangeMap.putCoalescing(NumberRange.closedOpen(0, 24), "b");
var result = Array.from(rangeMap.asMapOfRanges().entries());
expect(result.length).toBe(1);
expect(result[0][0].toString()).toBe("[0..24)");
expect(result[0][1]).toBe("b");
});
});
});
describe("asMapOfValues", function () {
it("should work", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(2, 10), 4);
rangeMap.putCoalescing(NumberRange.closedOpen(13, 16), 4);
rangeMap.putCoalescing(NumberRange.closedOpen(3, 12), 3);
var result = rangeMap.asMapOfValues();
var result3 = result.get(3);
var result4 = result.get(4);
expect(result4.length).toBe(2);
expect(result4[0].toString()).toBe("[2..3)");
expect(result4[1].toString()).toBe("[13..16)");
expect(result3.length).toBe(1);
expect(result3[0].toString()).toBe("[3..12)");
});
});
describe("subRangeMap", function () {
it("should work", function () {
var rangeMap = new RangeMap();
rangeMap.putCoalescing(NumberRange.closedOpen(2, 10), 4);
rangeMap.putCoalescing(NumberRange.closedOpen(13, 16), 4);
rangeMap.putCoalescing(NumberRange.closedOpen(3, 12), 3);
var subRangeMap = rangeMap.subRangeMap(NumberRange.closedOpen(2.5, 11));
var result = subRangeMap.asMapOfValues();
var result3 = result.get(3);
var result4 = result.get(4);
expect(result4.length).toBe(1);
expect(result4[0].toString()).toBe("[2.5..3)");
expect(result3.length).toBe(1);
expect(result3[0].toString()).toBe("[3..11)");
});
});
describe("remove", function () {
it('should remove an identical range', function () {
var rangeMap = new RangeMap();
var range = NumberRange.closedOpen(2, 10);
rangeMap.putCoalescing(range, 4);
rangeMap.remove(range);
expect(rangeMap.asMapOfRanges().size).toBe(0);
});
it('should remove encosed range', function () {
var rangeMap = new RangeMap();
var itemRange = NumberRange.closedOpen(3, 9);
var removeRange = NumberRange.closedOpen(2, 10);
rangeMap.putCoalescing(itemRange, 4);
rangeMap.remove(removeRange);
expect(rangeMap.asMapOfRanges().size).toBe(0);
});
it('should truncate range when partial overlapped', function () {
var rangeMap = new RangeMap();
var itemRange = NumberRange.closedOpen(1, 5);
var removeRange = NumberRange.closedOpen(3, 10);
rangeMap.putCoalescing(itemRange, 4);
rangeMap.remove(removeRange);
var result = rangeMap.asMapOfRanges();
expect(result.size).toBe(1);
expect(__spreadArray([], __read(result.entries()), false)[0]).toEqual([NumberRange.closedOpen(1, 3), 4]);
});
it('should truncate range when contained', function () {
var rangeMap = new RangeMap();
var itemRange = NumberRange.closedOpen(1, 10);
var removeRange = NumberRange.closedOpen(3, 5);
rangeMap.putCoalescing(itemRange, 4);
rangeMap.remove(removeRange);
var result = rangeMap.asMapOfRanges();
expect(result.size).toBe(2);
expect(__spreadArray([], __read(result.entries()), false)[0]).toEqual([NumberRange.closedOpen(1, 3), 4]);
expect(__spreadArray([], __read(result.entries()), false)[1]).toEqual([NumberRange.closedOpen(5, 10), 4]);
});
it('should not touch connected but not overlapping ranges', function () {
var rangeMap = new RangeMap();
var itemRange = NumberRange.closedOpen(1, 5);
var removeRange = NumberRange.closedOpen(5, 10);
rangeMap.putCoalescing(itemRange, 4);
rangeMap.remove(removeRange);
var result = rangeMap.asMapOfRanges();
expect(result.size).toBe(1);
expect(__spreadArray([], __read(result.entries()), false)[0]).toEqual([NumberRange.closedOpen(1, 5), 4]);
});
it('should not touch non-overlapping ranges', function () {
var rangeMap = new RangeMap();
var itemRange = NumberRange.closedOpen(1, 3);
var removeRange = NumberRange.closedOpen(5, 10);
rangeMap.putCoalescing(itemRange, 4);
rangeMap.remove(removeRange);
var result = rangeMap.asMapOfRanges();
expect(result.size).toBe(1);
expect(__spreadArray([], __read(result.entries()), false)[0]).toEqual([NumberRange.closedOpen(1, 3), 4]);
});
});
describe('issues', function () {
it('should correctly preserve positive and negative infinity range endpoints wwith putCoalescing', function () {
var rangeMap = new RangeMap(isEqual);
rangeMap.put(NumberRange.all(), []);
rangeMap.putCoalescing(NumberRange.closedOpen(1834354800000, 1834441200000), 'ab934de2-99be-4d01-8086-9b21082665c9');
check();
rangeMap.putCoalescing(NumberRange.closedOpen(1751320800000, 1751493600000), 'e4fe9b11-b33b-4a9d-8f82-893fa543d8ed');
check();
rangeMap.putCoalescing(NumberRange.closedOpen(1751320800000, 1752098400000), 'a546e6f6-796c-45c9-9ea1-91610d23ef04');
check();
function check() {
var resultEntries = __spreadArray([], __read(rangeMap.asMapOfRanges().entries()), false);
expect(resultEntries.some(function (_a) {
var _b = __read(_a, 2), range = _b[0], value = _b[1];
return range.lowerEndpoint === Number.NEGATIVE_INFINITY;
})).toBeTrue();
expect(resultEntries.some(function (_a) {
var _b = __read(_a, 2), range = _b[0], value = _b[1];
return range.upperEndpoint === Number.POSITIVE_INFINITY;
})).toBeTrue();
}
});
it('should return the correct span if the same range is added multiple times with put', function () {
var rangeMap = new RangeMap(isEqual);
rangeMap.put(NumberRange.closedOpen(1590962400000, 1622498400000), 1);
rangeMap.put(NumberRange.closedOpen(1590962400000, 1622498400000), 2);
rangeMap.put(NumberRange.closedOpen(1590962400000, 1622498400000), 3);
rangeMap.put(NumberRange.closedOpen(1616281200000, 1648076400000), 4);
expect(rangeMap.span()).toEqual(NumberRange.closedOpen(1590962400000, 1648076400000));
});
});
});
describe("README example", function () {
it("should work", function () {
var festivalAttendanceRangeMap = new RangeMap(isEqual);
var attendance = [
{
name: "Bob",
days: [1, 2, 3, 4],
},
{
name: "Lisa",
days: [1, 2, 3],
},
{
name: "Eve",
days: [4, 1],
},
];
festivalAttendanceRangeMap.putCoalescing(NumberRange.closedOpen(1, 5), []);
attendance.forEach(function (_a) {
var name = _a.name, days = _a.days;
days.forEach(function (day) {
var dayRange = NumberRange.closedOpen(day, day + 1);
var subRangeMap = festivalAttendanceRangeMap
.subRangeMap(dayRange)
.asMapOfRanges();
__spreadArray([], __read(subRangeMap.entries()), false).forEach(function (_a) {
var _b = __read(_a, 2), key = _b[0], value = _b[1];
festivalAttendanceRangeMap.putCoalescing(key, __spreadArray(__spreadArray([], __read(value), false), [name], false));
});
});
});
var result = festivalAttendanceRangeMap.asMapOfRanges();
expect(__spreadArray([], __read(result.entries()), false)).toEqual([
[NumberRange.closedOpen(1, 2), ["Bob", "Lisa", "Eve"]],
[NumberRange.closedOpen(2, 4), ["Bob", "Lisa"]],
[NumberRange.closedOpen(4, 5), ["Bob", "Eve"]],
]);
});
});
//# sourceMappingURL=range-map.spec.js.map