Skip to content

Commit d233225

Browse files
committed
feat: add extra properties on complement and substract
1 parent b4bc0d5 commit d233225

File tree

2 files changed

+58
-26
lines changed

2 files changed

+58
-26
lines changed

lib/lib.test.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,16 @@ test('will substract two arrays', t => {
271271
testFnToIntervals(base, mask, substract, testOutputFn, t);
272272
});
273273

274+
test('substract will keep extra properties from base', t => {
275+
const base = [{ start: 0, end: 10, test: 'foo' }, { start: 12, end: 20, test: 'bar' }];
276+
const mask = [{ start: 1, end: 3 }, { start: 8, end: 13 }, { start: 18, end: 22 }];
277+
const res = substract(base, mask);
278+
t.true(res.length === 3);
279+
t.true(res[0].start === 0 && res[0].end === 1 && res[0].test === 'foo');
280+
t.true(res[1].start === 3 && res[1].end === 8 && res[1].test === 'foo');
281+
t.true(res[2].start === 13 && res[2].end === 18 && res[2].test === 'bar');
282+
});
283+
274284
test('will return complement', t => {
275285
const intervals = [{ start: 1, end: 2 }, { start: 5, end: 7 }, { start: 6, end: 8 }];
276286
const boundaries = { start: 0, end: 10 };
@@ -283,6 +293,16 @@ test('will return complement', t => {
283293
testFnToIntervals(boundaries, intervals, complement, testOutputFn, t);
284294
});
285295

296+
test('complement will keep additional properties from boundaries', t => {
297+
const intervals = [{ start: 1, end: 2 }, { start: 5, end: 7 }, { start: 6, end: 8 }];
298+
const boundaries = { start: 0, end: 10, test: 'foo' };
299+
const res = complement(boundaries, intervals);
300+
t.true(res.length === 3);
301+
t.true(res[0].start === 0 && res[0].end === 1 && res[0].test === 'foo');
302+
t.true(res[1].start === 2 && res[1].end === 5 && res[0].test === 'foo');
303+
t.true(res[2].start === 8 && res[2].end === 10 && res[0].test === 'foo');
304+
});
305+
286306
test('will return complement when boundaries are included in intervals', t => {
287307
const intervals = [{ start: 1, end: 2 }, { start: 5, end: 7 }, { start: 6, end: 8 }];
288308
const boundaries = { start: 2, end: 6 };
@@ -404,10 +424,10 @@ test('will intersect an interval and an array', t => {
404424

405425
test('intersection will not simplify', t => {
406426
const r1 = [{ start: 1, end: 5 }];
407-
const r2 = [{ start: 1, end: 2 }, { start: 2, end: 5 }];
427+
const r2 = [{ start: 1, end: 3 }, { start: 2, end: 5 }];
408428
const testOutputFn = (res: IntervalSE[]): void => {
409429
t.true(res.length === 2);
410-
t.true(res[0].start === 1 && res[0].end === 2);
430+
t.true(res[0].start === 1 && res[0].end === 3);
411431
t.true(res[1].start === 2 && res[1].end === 5);
412432
};
413433
testFnToIntervals(r1, r2, intersect, testOutputFn, t);
@@ -431,3 +451,13 @@ test('intersection will keep object properties when truncated', t => {
431451
t.true(res[0].test === 'bar');
432452
t.true(res[1].test === 'baz');
433453
});
454+
455+
test('will trash empty interval', t => {
456+
const r1 = [{ start: 0, end: 5, test: 'foo' }, { start: 8, end: 10, test: 'bar' }];
457+
const r2 = { start: 6, end: 9 };
458+
const res = intersect(r2, r1);
459+
t.is(res.length, 1);
460+
t.is(res[0].start, 8);
461+
t.is(res[0].end, 9);
462+
t.is(res[0].test, 'bar');
463+
});

lib/lib.ts

+26-24
Original file line numberDiff line numberDiff line change
@@ -85,32 +85,33 @@ const prepareInput = (typeStr: string, i: interval | roai): IntervalSE[] => {
8585

8686
const complementGen = (boundaries: IntervalSE, intervals: IntervalSE[]): IntervalSE[] => {
8787
const intervalsS = sortByStart(intervals);
88+
const { start, end, ...rest} = boundaries;
8889
const prepRanges: IntervalSE[] = [
89-
{ start: -Infinity, end: boundaries.start },
90+
{ start: -Infinity, end: start },
9091
...intervalsS,
91-
{ start: boundaries.end, end: Infinity },
92+
{ start: end, end: Infinity },
9293
];
9394
return reject<IntervalSE | null>(
9495
isNil,
9596
aperture(2, prepRanges).map(
96-
([r1, r2]) => (r1.end >= r2.start ? null : { start: r1.end, end: r2.start })
97+
([r1, r2]) => (r1.end >= r2.start ? null : { start: r1.end, end: r2.start, ...rest })
9798
)
9899
) as IntervalSE[];
99100
};
100101

101-
const complementCurry = <T extends interval>(boundaries: interval, intervals: T | T[]): T[] => {
102+
const complementCurry = <D extends interval, T extends interval>(boundaries: D, intervals: T | T[]): D[] => {
102103
if (Array.isArray(intervals) && intervals.length < 1) {
103-
return [boundaries] as T[];
104+
return [boundaries] as D[];
104105
}
105-
const typeStr = getType(intervals);
106+
const typeStr = getType(boundaries);
106107
const intervalSE = prepareInput(typeStr, intervals);
107108
const boundariesSE = convertFrom(getType(boundaries))(boundaries);
108-
return complementGen(boundariesSE, intervalSE).map(convertTo<T>(typeStr));
109+
return complementGen(boundariesSE, intervalSE).map(convertTo<D>(typeStr));
109110
};
110111

111112
/**
112113
* Complement of `intervals` bounded to `boundaries`. Convert space between two consecutive intervals into interval.
113-
*
114+
* Keeps extra object properties on `boundaries`.
114115
* Curried function. Accept array of intervals. Doesn't mutate input. Output keeps input's structure.
115116
*
116117
* boundaries | interval(s) | result
@@ -122,16 +123,16 @@ const complementCurry = <T extends interval>(boundaries: interval, intervals: T
122123
* @param intervals arg2: one interval or array of intervals that complement the result.
123124
* @returns array of intervals.
124125
*/
125-
export function complement<T extends interval>(boundaries: interval, interv: T | roat<T>): T[];
126-
export function complement<T extends interval>(boundaries: interval): (interv: T | roat<T>) => T[];
127-
export function complement<T extends interval>(boundaries: interval, interv?: T | roat<T>): any {
126+
export function complement<D extends interval, T extends interval>(boundaries: D, interv: T | roat<T>): D[];
127+
export function complement<D extends interval, T extends interval>(boundaries: D): (interv: T | roat<T>) => D[];
128+
export function complement<D extends interval, T extends interval>(boundaries: D, interv?: T | roat<T>): any {
128129
switch (arguments.length) {
129130
case 1:
130-
return (tt2: T | T[]): T[] => {
131-
return complementCurry<T>(boundaries, tt2);
131+
return (tt2: T | T[]): D[] => {
132+
return complementCurry<D, T>(boundaries, tt2);
132133
};
133134
case 2:
134-
return complementCurry<T>(boundaries, interv as T | T[]);
135+
return complementCurry<D, T>(boundaries, interv as T | T[]);
135136
}
136137
}
137138

@@ -634,14 +635,15 @@ const subtractInter = (mask: IntervalSE[], base: IntervalSE): IntervalSE[] => {
634635
};
635636

636637
const substractGen = (base: IntervalSE[], mask: IntervalSE[]): IntervalSE[] => {
637-
const intersection = intersectGen(base, mask);
638+
const intersection = intersectGen(mask, base);
638639
return unnest(
639640
base.map(b => subtractInter(intersection.filter(isOverlappingSimple.bind(null, b)), b))
640641
);
641642
};
642643

643644
/**
644645
* Subtact `intervalA` with `intervalB`. `intervalB` act as a mask.
646+
* Keeps extra object properties on `intervalA`.
645647
*
646648
* Curried function. Accept array of intervals. Doesn't mutate input. Output keeps input's structure.
647649
*
@@ -654,20 +656,20 @@ const substractGen = (base: IntervalSE[], mask: IntervalSE[]): IntervalSE[] => {
654656
* @param intervalB arg2: one interval or array of intervals
655657
* @returns intersection of `arg1` and `arg2`
656658
*/
657-
export function substract<T extends interval>(intervalA: T | roat<T>, intervalB: T | roat<T>): T[];
658-
export function substract<T extends interval>(
659-
intervalA: T | roat<T>
660-
): (intervalB: T | roat<T>) => T[];
661-
export function substract<T extends interval>(
662-
intervalA: T | roat<T>,
659+
export function substract<D extends interval, T extends interval>(intervalA: D | roat<D>, intervalB: T | roat<T>): D[];
660+
export function substract<D extends interval, T extends interval>(
661+
intervalA: D | roat<D>
662+
): (intervalB: T | roat<T>) => D[];
663+
export function substract<D extends interval, T extends interval>(
664+
intervalA: D | roat<D>,
663665
intervalB?: T | roat<T>
664666
): any {
665667
switch (arguments.length) {
666668
case 1:
667-
return (tt2: T | T[]): T[] => {
668-
return setupForTwoIntsToInts<T>(substractGen)(intervalA, tt2);
669+
return (tt2: T | T[]): D[] => {
670+
return setupForTwoIntsToInts<D>(substractGen)(intervalA, tt2 as any);
669671
};
670672
case 2:
671-
return setupForTwoIntsToInts<T>(substractGen)(intervalA, intervalB as T | T[]);
673+
return setupForTwoIntsToInts<D>(substractGen)(intervalA, intervalB as any);
672674
}
673675
}

0 commit comments

Comments
 (0)