Skip to content

Commit 2a21f78

Browse files
committed
feat: add split operator
1 parent d233225 commit 2a21f78

File tree

3 files changed

+106
-13
lines changed

3 files changed

+106
-13
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ All function are curried. Output keeps the input's structure and type. No input
2222
### Manipulations
2323
* Complement *
2424
* Merge
25+
* Split *
2526
### Tests ([Allen's interval relations](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra))
2627
* After
2728
* Before
@@ -45,10 +46,10 @@ Functions marked with `*` both accept interval and Array/ReadonlyArray of interv
4546

4647
## Installation
4748

48-
Intervals-fn is distributed on the dev and latest channel:
49+
Intervals-fn is distributed on the latest channel:
4950

5051
```bash
51-
$ npm install intervals-fn@dev --save
52+
$ npm install intervals-fn --save
5253
```
5354

5455
## Advenced docs

lib/lib.test.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import test from 'ava';
2-
1+
import test, { TestContext } from 'ava';
32
import { IntervalAR, IntervalFT, IntervalSE } from './data.structures';
43
import {
54
complement,
@@ -14,6 +13,7 @@ import {
1413
isStarting,
1514
merge,
1615
simplify,
16+
split,
1717
substract,
1818
unify,
1919
} from './lib';
@@ -71,6 +71,17 @@ const testFnToIntervals = (
7171
t.throws(fn.bind(null, [{ test: 1 }], { test: 1 }), 'Unrecognized interval format');
7272
};
7373

74+
const testInterval = (t: TestContext, i: IntervalSE, vals: [number, number], extra: object) => {
75+
t.is(i.start, vals[0], `test interval: [${i.start}, ${i.end}]`);
76+
t.is(i.end, vals[1], `test interval: ${i}`);
77+
Object.entries(extra).forEach(([key, val]) => {
78+
t.true(
79+
i.hasOwnProperty(key) && Reflect.get(i, key) === val,
80+
`test interval: ${i}, with prop: ${key}:${val}`
81+
);
82+
});
83+
};
84+
7485
interface IWithData {
7586
data: number;
7687
}
@@ -461,3 +472,22 @@ test('will trash empty interval', t => {
461472
t.is(res[0].end, 9);
462473
t.is(res[0].test, 'bar');
463474
});
475+
476+
test('will split empty interval', t => {
477+
const r1: IntervalSE[] = [];
478+
const r2 = [5];
479+
const res = split(r2)(r1);
480+
t.is(res.length, 0);
481+
});
482+
483+
484+
test('will split intervals', t => {
485+
const r1 = [{ start: 0, end: 7, test: 'foo' }, { start: 3, end: 8, test: 'bar' }];
486+
const r2 = [5];
487+
const res = split(r2, r1);
488+
t.is(res.length, 4);
489+
testInterval(t, res[0], [0, 5], { test: 'foo' });
490+
testInterval(t, res[1], [5, 7], { test: 'foo' });
491+
testInterval(t, res[2], [3, 5], { test: 'bar' });
492+
testInterval(t, res[3], [5, 8], { test: 'bar' });
493+
});

lib/lib.ts

+71-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
any,
33
aperture,
44
applySpec,
5+
chain,
56
concat,
67
converge,
78
dissoc,
@@ -85,7 +86,7 @@ const prepareInput = (typeStr: string, i: interval | roai): IntervalSE[] => {
8586

8687
const complementGen = (boundaries: IntervalSE, intervals: IntervalSE[]): IntervalSE[] => {
8788
const intervalsS = sortByStart(intervals);
88-
const { start, end, ...rest} = boundaries;
89+
const { start, end, ...rest } = boundaries;
8990
const prepRanges: IntervalSE[] = [
9091
{ start: -Infinity, end: start },
9192
...intervalsS,
@@ -99,7 +100,10 @@ const complementGen = (boundaries: IntervalSE, intervals: IntervalSE[]): Interva
99100
) as IntervalSE[];
100101
};
101102

102-
const complementCurry = <D extends interval, T extends interval>(boundaries: D, intervals: T | T[]): D[] => {
103+
const complementCurry = <D extends interval, T extends interval>(
104+
boundaries: D,
105+
intervals: T | T[]
106+
): D[] => {
103107
if (Array.isArray(intervals) && intervals.length < 1) {
104108
return [boundaries] as D[];
105109
}
@@ -123,9 +127,17 @@ const complementCurry = <D extends interval, T extends interval>(boundaries: D,
123127
* @param intervals arg2: one interval or array of intervals that complement the result.
124128
* @returns array of intervals.
125129
*/
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 {
130+
export function complement<D extends interval, T extends interval>(
131+
boundaries: D,
132+
interv: T | roat<T>
133+
): D[];
134+
export function complement<D extends interval, T extends interval>(
135+
boundaries: D
136+
): (interv: T | roat<T>) => D[];
137+
export function complement<D extends interval, T extends interval>(
138+
boundaries: D,
139+
interv?: T | roat<T>
140+
): any {
129141
switch (arguments.length) {
130142
case 1:
131143
return (tt2: T | T[]): D[] => {
@@ -502,9 +514,9 @@ const intersectUnfolderSeed = (
502514
return [new1, new2];
503515
};
504516

505-
const intersectUnfolder = (
506-
[inters1, inters2]: [IntervalSE[], IntervalSE[]]
507-
): false | [IntervalSE | null, [IntervalSE[], IntervalSE[]]] => {
517+
const intersectUnfolder = ([inters1, inters2]: [IntervalSE[], IntervalSE[]]):
518+
| false
519+
| [IntervalSE | null, [IntervalSE[], IntervalSE[]]] => {
508520
if (any(isEmpty)([inters1, inters2])) {
509521
return false;
510522
}
@@ -656,7 +668,10 @@ const substractGen = (base: IntervalSE[], mask: IntervalSE[]): IntervalSE[] => {
656668
* @param intervalB arg2: one interval or array of intervals
657669
* @returns intersection of `arg1` and `arg2`
658670
*/
659-
export function substract<D extends interval, T extends interval>(intervalA: D | roat<D>, intervalB: T | roat<T>): D[];
671+
export function substract<D extends interval, T extends interval>(
672+
intervalA: D | roat<D>,
673+
intervalB: T | roat<T>
674+
): D[];
660675
export function substract<D extends interval, T extends interval>(
661676
intervalA: D | roat<D>
662677
): (intervalB: T | roat<T>) => D[];
@@ -673,3 +688,50 @@ export function substract<D extends interval, T extends interval>(
673688
return setupForTwoIntsToInts<D>(substractGen)(intervalA, intervalB as any);
674689
}
675690
}
691+
692+
const numberToRange = (n: number): IntervalSE => ({ start: n, end: n });
693+
694+
const splitGen = (splits: roat<number>, intervals: IntervalSE[]): IntervalSE[] => {
695+
return chain((i => {
696+
return chain((int => [
697+
{ ...int, start: int.start, end: i },
698+
{ ...int, start: i, end: int.end },
699+
]), intervals.filter(int => isOverlappingSimple(int, numberToRange(i))));
700+
}), splits);
701+
};
702+
703+
const splitCurry = <T extends interval>(splitIndexes: roat<number>, intervals: T | T[]): T[] => {
704+
if (Array.isArray(intervals) && intervals.length < 1) {
705+
return [];
706+
}
707+
const typeStr = getType(intervals);
708+
const intervalSE = prepareInput(typeStr, intervals);
709+
return splitGen(splitIndexes, intervalSE).map(convertTo<T>(typeStr));
710+
};
711+
712+
/**
713+
* Split `intervals` with `splitIndexes`.
714+
* Keeps extra object properties on `intervals`.
715+
* Curried function. Accept array of intervals. Doesn't mutate input. Output keeps input's structure.
716+
*
717+
* splitIndexes | interval(s) | result
718+
* --- | --- | ---
719+
* [2, 4] | { start: 0, end: 6, foo: 'bar' } | [{ start: 0, end: 2, foo: 'bar' }, { start: 2, end: 4, foo: 'bar' } { start: 4, end: 6, foo: 'bar' }]
720+
* [5] | [{ start: 0, end: 7 }, { start: 3, end: 8 }] | [{ start: 0, end: 5 }, { start: 5, end: 7 }, { start: 3, end: 5 }, { start: 5, end: 8 }]
721+
*
722+
* @param splitIndexes arg1: defines indexes where intervals are splitted.
723+
* @param intervals arg2: intervals to be splitted.
724+
* @returns array of intervals.
725+
*/
726+
export function split<T extends interval>(splitIndexes: roat<number>, interv: T | roat<T>): T[];
727+
export function split<T extends interval>(splitIndexes: roat<number>): (interv: T | roat<T>) => T[];
728+
export function split<T extends interval>(splitIndexes: roat<number>, interv?: T | roat<T>): any {
729+
switch (arguments.length) {
730+
case 1:
731+
return (tt2: T | T[]): T[] => {
732+
return splitCurry<T>(splitIndexes, tt2);
733+
};
734+
case 2:
735+
return splitCurry<T>(splitIndexes, interv as T | T[]);
736+
}
737+
}

0 commit comments

Comments
 (0)