range-ts
Version:
RangeMap implementation based on Guava
135 lines • 5.26 kB
JavaScript
import { NumberRange } from "../number-range/number-range";
import { BoundType } from "../core/bound-type";
export class RangeMap {
static fromRangeValues(values, eq) {
const rangeMap = new RangeMap(eq);
rangeMap.rangeValues = values;
return rangeMap;
}
constructor(eq = (a, b) => a === b) {
this.eq = eq;
this.rangeValues = [];
}
put(range, value) {
this.combinedPut(range, value, false);
}
putCoalescing(range, value) {
this.combinedPut(range, value, true);
}
get(value) {
const foundRangeValue = this.rangeValues.find((currentRangeValue) => currentRangeValue.range.contains(value));
if (foundRangeValue) {
return foundRangeValue.value;
}
return null;
}
asMapOfRanges() {
const newMap = new Map();
this.rangeValues
.filter((range) => !range.range.isEmpty())
.sort((a, b) => a.range.lowerEndpoint.valueOf() - b.range.lowerEndpoint.valueOf())
.forEach((currentRangeValue) => {
newMap.set(currentRangeValue.range, currentRangeValue.value);
});
return newMap;
}
asMapOfValues() {
const newMap = new Map();
this.rangeValues
.filter((range) => !range.range.isEmpty())
.sort((a, b) => a.range.lowerEndpoint.valueOf() - b.range.lowerEndpoint.valueOf())
.forEach((currentRangeValue) => {
if (newMap.has(currentRangeValue.value)) {
newMap.get(currentRangeValue.value).push(currentRangeValue.range);
}
else {
newMap.set(currentRangeValue.value, [currentRangeValue.range]);
}
});
return newMap;
}
subRangeMap(range) {
const rangeValues = this.rangeValues.flatMap((rangeValue) => {
const intersection = rangeValue.range.intersection(range);
if (!intersection) {
return [];
}
return [
{
range: intersection,
value: rangeValue.value,
},
];
});
return RangeMap.fromRangeValues(rangeValues);
}
getEntry(key) {
const foundRangeValue = this.rangeValues.find((currentRangeValue) => currentRangeValue.range.contains(key));
if (foundRangeValue) {
return [foundRangeValue.range, foundRangeValue.value];
}
return null;
}
span() {
if (this.rangeValues.length === 0) {
return null;
}
const sortedRangeValues = this.rangeValues.sort((a, b) => a.range.lowerEndpoint.valueOf() - b.range.lowerEndpoint.valueOf());
return new NumberRange(sortedRangeValues[0].range.lowerEndpoint, sortedRangeValues[0].range.lowerBoundType, sortedRangeValues[sortedRangeValues.length - 1].range.upperEndpoint, sortedRangeValues[sortedRangeValues.length - 1].range.upperBoundType);
}
remove(range) {
if (range.isEmpty()) {
return;
}
const toDelete = {
toDeleteId: Math.random()
};
this.put(range, toDelete);
this.rangeValues = this.rangeValues.filter(rangeValue => rangeValue.value !== toDelete);
}
combinedPut(range, value, shouldPutCoalescing = false) {
let newRange = range;
let affectedRangeValues = [];
const unaffectedRangeValues = [];
this.rangeValues.forEach((currentRangeValue) => {
if (currentRangeValue.range.isConnected(newRange)) {
affectedRangeValues.push(currentRangeValue);
}
else {
unaffectedRangeValues.push(currentRangeValue);
}
});
affectedRangeValues = affectedRangeValues.flatMap((currentRangeValue) => {
var _a;
if (shouldPutCoalescing && this.eq(value, currentRangeValue.value)) {
newRange = newRange.span(currentRangeValue.range);
return [];
}
if ((_a = currentRangeValue.range.intersection(newRange)) === null || _a === void 0 ? void 0 : _a.isEmpty()) {
return [currentRangeValue];
}
const rangeBefore = currentRangeValue.range.intersection(NumberRange.upTo(newRange.lowerEndpoint, newRange.lowerBoundType === BoundType.OPEN
? BoundType.CLOSED
: BoundType.OPEN));
const rangeAfter = currentRangeValue.range.intersection(new NumberRange(newRange.upperEndpoint, newRange.upperBoundType === BoundType.OPEN
? BoundType.CLOSED
: BoundType.OPEN, Number.POSITIVE_INFINITY, BoundType.OPEN));
return [rangeBefore, rangeAfter]
.filter((a) => !!a)
.filter(a => !a.isEmpty())
.map((currentRange) => ({
range: currentRange,
value: currentRangeValue.value,
}));
});
this.rangeValues = [
...unaffectedRangeValues,
...affectedRangeValues,
{
range: newRange,
value,
},
];
}
}
//# sourceMappingURL=range-map.js.map