@genome-spy/core 0.43.1 → 0.43.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle/index.es.js +236 -226
- package/dist/bundle/index.js +38 -28
- package/dist/src/data/sources/lazy/bigBedSource.js +27 -6
- package/dist/src/data/transforms/coverage.d.ts +10 -2
- package/dist/src/data/transforms/coverage.d.ts.map +1 -1
- package/dist/src/data/transforms/coverage.js +22 -9
- package/dist/src/data/transforms/coverage.test.js +202 -87
- package/dist/src/utils/kWayMerge.d.ts +3 -2
- package/dist/src/utils/kWayMerge.d.ts.map +1 -1
- package/dist/src/utils/kWayMerge.js +4 -3
- package/dist/src/utils/kWayMerge.test.js +5 -1
- package/package.json +2 -2
|
@@ -97,7 +97,8 @@ export default class BigBedSource extends SingleAxisWindowedSource {
|
|
|
97
97
|
* This parser avoids generating piles of garbage to be collected by the GC.
|
|
98
98
|
* We don't split the line into an array of strings, but instead parse the
|
|
99
99
|
* integer fields directly from the original string.
|
|
100
|
-
* This parser doesn't support arrays, etc. at the moment.
|
|
100
|
+
* This parser doesn't support arrays, etc. at the moment. This could, however,
|
|
101
|
+
* be extended into a fully-featured parser.
|
|
101
102
|
*
|
|
102
103
|
* @param {import("@gmod/bed").default} bed
|
|
103
104
|
*/
|
|
@@ -171,6 +172,30 @@ function makeFastParser(bed) {
|
|
|
171
172
|
}
|
|
172
173
|
});
|
|
173
174
|
|
|
175
|
+
const templateFields = fields.map(
|
|
176
|
+
(field) => `"${field.name}": ${field.isNumeric ? "0" : "emptyString"}`
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Make a template object with all fields to avoid the JavaScript VM's
|
|
181
|
+
* hidden class to be changed after each property assignment. Transitions
|
|
182
|
+
* between hidden classes generate plenty of garbage to be collected.
|
|
183
|
+
*
|
|
184
|
+
* Ideally, the parsed values would be assigned directly in this function,
|
|
185
|
+
* but for some reason, it results in abysmally slow performance on Chrome,
|
|
186
|
+
* but not on Firefox, where it would be super fast.
|
|
187
|
+
*/
|
|
188
|
+
const makeTemplate = new Function(`
|
|
189
|
+
const emptyString = "";
|
|
190
|
+
return function makeTemplate(chrom, chromStart, chromEnd) {
|
|
191
|
+
return {
|
|
192
|
+
chrom,
|
|
193
|
+
chromStart,
|
|
194
|
+
chromEnd,
|
|
195
|
+
${templateFields.join(",\n")}
|
|
196
|
+
}
|
|
197
|
+
};`)();
|
|
198
|
+
|
|
174
199
|
/**
|
|
175
200
|
* @param {string} line
|
|
176
201
|
*/
|
|
@@ -189,11 +214,7 @@ function makeFastParser(bed) {
|
|
|
189
214
|
function parseLine(chrom, chromStart, chromEnd, rest) {
|
|
190
215
|
setLine(rest);
|
|
191
216
|
|
|
192
|
-
currentObject =
|
|
193
|
-
chrom,
|
|
194
|
-
chromStart,
|
|
195
|
-
chromEnd,
|
|
196
|
-
};
|
|
217
|
+
currentObject = makeTemplate(chrom, chromStart, chromEnd);
|
|
197
218
|
|
|
198
219
|
for (let j = 0, n = fieldParsers.length; j < n; j++) {
|
|
199
220
|
fieldParsers[j]();
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Computes coverage for sorted segments
|
|
4
4
|
*
|
|
5
|
-
* TODO: Binned coverage
|
|
5
|
+
* TODO: Binned coverage, e.g., don't emit a new segment for every
|
|
6
|
+
* coverage change, but only every n bases or so. The most straightforward
|
|
7
|
+
* way to implement it is a separate transform that bins the coverage
|
|
8
|
+
* segments and calculates weighted averages.
|
|
6
9
|
*/
|
|
7
10
|
export default class CoverageTransform extends FlowNode {
|
|
8
11
|
/**
|
|
@@ -23,7 +26,12 @@ export default class CoverageTransform extends FlowNode {
|
|
|
23
26
|
chrom: string;
|
|
24
27
|
};
|
|
25
28
|
createSegment: Function;
|
|
26
|
-
|
|
29
|
+
/**
|
|
30
|
+
* End pos as priority, weight as value
|
|
31
|
+
*
|
|
32
|
+
* @type {FlatQueue<number>}
|
|
33
|
+
*/
|
|
34
|
+
ends: FlatQueue<number>;
|
|
27
35
|
}
|
|
28
36
|
import FlowNode from "../flowNode.js";
|
|
29
37
|
import FlatQueue from "flatqueue";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/coverage.js"],"names":[],"mappings":";AAKA
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/coverage.js"],"names":[],"mappings":";AAKA;;;;;;;GAOG;AACH;IAKI;;OAEG;IACH,oBAFW,OAAO,yBAAyB,EAAE,cAAc,EAgD1D;IA5CG,yDAAoB;IAEpB,mDAAwC;IACxC,iDAAoC;IAEpC,mCAAmC;IACnC,sBADoB,GAAG,KAAE,MAAM,CAGT;IACtB,mCAAmC;IACnC,uBADoB,GAAG,KAAE,MAAM,CACsC;IAErE;;;;;MAKC;IAGD,wBAgBC;IAED;;;;OAIG;IACH,MAFU,UAAU,MAAM,CAAC,CAEA;CAiIlC;qBAhMyC,gBAAgB;sBAHpC,WAAW"}
|
|
@@ -6,7 +6,10 @@ import FlowNode, { BEHAVIOR_CLONES } from "../flowNode.js";
|
|
|
6
6
|
/**
|
|
7
7
|
* Computes coverage for sorted segments
|
|
8
8
|
*
|
|
9
|
-
* TODO: Binned coverage
|
|
9
|
+
* TODO: Binned coverage, e.g., don't emit a new segment for every
|
|
10
|
+
* coverage change, but only every n bases or so. The most straightforward
|
|
11
|
+
* way to implement it is a separate transform that bins the coverage
|
|
12
|
+
* segments and calculates weighted averages.
|
|
10
13
|
*/
|
|
11
14
|
export default class CoverageTransform extends FlowNode {
|
|
12
15
|
get behavior() {
|
|
@@ -56,7 +59,11 @@ export default class CoverageTransform extends FlowNode {
|
|
|
56
59
|
)
|
|
57
60
|
);
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
/**
|
|
63
|
+
* End pos as priority, weight as value
|
|
64
|
+
*
|
|
65
|
+
* @type {FlatQueue<number>}
|
|
66
|
+
*/
|
|
60
67
|
this.ends = new FlatQueue();
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -75,7 +82,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
75
82
|
const chromAccessor = this.chromAccessor;
|
|
76
83
|
const weightAccessor = this.weightAccessor;
|
|
77
84
|
|
|
78
|
-
/** @type {
|
|
85
|
+
/** @type {import("../flowNode.js").Datum} used for merging adjacent segment */
|
|
79
86
|
let bufferedSegment;
|
|
80
87
|
|
|
81
88
|
/** @type {string} */
|
|
@@ -91,7 +98,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
91
98
|
/** @type {number} */
|
|
92
99
|
let prevEdge;
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
/** End pos as priority, weight as value */
|
|
95
102
|
const ends = this.ends;
|
|
96
103
|
ends.clear();
|
|
97
104
|
|
|
@@ -127,9 +134,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
127
134
|
};
|
|
128
135
|
|
|
129
136
|
const flushQueue = () => {
|
|
130
|
-
|
|
131
|
-
/** @type {number} */
|
|
132
|
-
let edge;
|
|
137
|
+
let edge = 0;
|
|
133
138
|
while ((edge = ends.peekValue()) !== undefined) {
|
|
134
139
|
pushSegment(prevEdge, edge, coverage);
|
|
135
140
|
prevEdge = edge;
|
|
@@ -147,8 +152,7 @@ export default class CoverageTransform extends FlowNode {
|
|
|
147
152
|
this.handle = (datum) => {
|
|
148
153
|
const start = startAccessor(datum);
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
let edge;
|
|
155
|
+
let edge = 0;
|
|
152
156
|
while ((edge = ends.peekValue()) !== undefined && edge < start) {
|
|
153
157
|
pushSegment(prevEdge, edge, coverage);
|
|
154
158
|
prevEdge = edge;
|
|
@@ -179,5 +183,14 @@ export default class CoverageTransform extends FlowNode {
|
|
|
179
183
|
flushQueue();
|
|
180
184
|
super.complete();
|
|
181
185
|
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {import("../../types/flowBatch.js").FlowBatch} flowBatch
|
|
189
|
+
*/
|
|
190
|
+
this.beginBatch = (flowBatch) => {
|
|
191
|
+
flushQueue();
|
|
192
|
+
prevChrom = undefined;
|
|
193
|
+
super.beginBatch(flowBatch);
|
|
194
|
+
};
|
|
182
195
|
}
|
|
183
196
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
2
|
import CoverageTransform from "./coverage.js";
|
|
3
3
|
import { processData } from "../flowTestUtils.js";
|
|
4
4
|
|
|
@@ -16,108 +16,223 @@ function transform(params, data) {
|
|
|
16
16
|
return processData(t, data);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
[8, 10],
|
|
26
|
-
[11, 14],
|
|
27
|
-
[11, 13],
|
|
28
|
-
[11, 12],
|
|
29
|
-
[15, 18],
|
|
30
|
-
[16, 18],
|
|
31
|
-
[17, 18],
|
|
32
|
-
].map((d) => ({
|
|
33
|
-
start: d[0],
|
|
34
|
-
end: d[1],
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
const coverageSegments = [
|
|
38
|
-
[0, 1, 1],
|
|
39
|
-
[1, 2, 2],
|
|
40
|
-
[2, 3, 3],
|
|
41
|
-
[3, 6, 2],
|
|
42
|
-
[6, 10, 1],
|
|
43
|
-
[11, 12, 3],
|
|
44
|
-
[12, 13, 2],
|
|
45
|
-
[13, 14, 1],
|
|
46
|
-
[15, 16, 1],
|
|
47
|
-
[16, 17, 2],
|
|
48
|
-
[17, 18, 3],
|
|
49
|
-
].map((d) => ({
|
|
50
|
-
start: d[0],
|
|
51
|
-
end: d[1],
|
|
52
|
-
coverage: d[2],
|
|
53
|
-
}));
|
|
54
|
-
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param {[number, number][]} reads Start, end
|
|
22
|
+
* @param {[number, number, number][]} coverageSegments Start, end, coverage
|
|
23
|
+
*/
|
|
24
|
+
function testSimpleCoverage(reads, coverageSegments) {
|
|
55
25
|
/** @type {CoverageParams} */
|
|
56
26
|
const coverageConfig = {
|
|
57
27
|
type: "coverage",
|
|
58
28
|
start: "start",
|
|
59
29
|
end: "end",
|
|
60
30
|
};
|
|
61
|
-
expect(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
31
|
+
expect(
|
|
32
|
+
transform(
|
|
33
|
+
coverageConfig,
|
|
34
|
+
reads.map((d) => ({
|
|
35
|
+
start: d[0],
|
|
36
|
+
end: d[1],
|
|
37
|
+
}))
|
|
38
|
+
)
|
|
39
|
+
).toEqual(
|
|
40
|
+
coverageSegments.map((d) => ({
|
|
41
|
+
start: d[0],
|
|
42
|
+
end: d[1],
|
|
43
|
+
coverage: d[2],
|
|
44
|
+
}))
|
|
45
|
+
);
|
|
46
|
+
}
|
|
76
47
|
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {[number, number, number][]} reads Start, end, weight
|
|
51
|
+
* @param {[number, number, number][]} coverageSemgments Start, end, coverage
|
|
52
|
+
*/
|
|
53
|
+
function testWeightedCoverage(reads, coverageSegments) {
|
|
77
54
|
/** @type {CoverageParams} */
|
|
78
55
|
const coverageConfig = {
|
|
79
56
|
type: "coverage",
|
|
80
|
-
chrom: "chrom",
|
|
81
57
|
start: "start",
|
|
82
58
|
end: "end",
|
|
59
|
+
weight: "weight",
|
|
83
60
|
};
|
|
61
|
+
expect(
|
|
62
|
+
transform(
|
|
63
|
+
coverageConfig,
|
|
64
|
+
reads.map((d) => ({
|
|
65
|
+
start: d[0],
|
|
66
|
+
end: d[1],
|
|
67
|
+
weight: d[2],
|
|
68
|
+
}))
|
|
69
|
+
)
|
|
70
|
+
).toEqual(
|
|
71
|
+
coverageSegments.map((d) => ({
|
|
72
|
+
start: d[0],
|
|
73
|
+
end: d[1],
|
|
74
|
+
coverage: d[2],
|
|
75
|
+
}))
|
|
76
|
+
);
|
|
77
|
+
}
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
describe("Coverage transform", () => {
|
|
80
|
+
test("Typical case", () =>
|
|
81
|
+
testSimpleCoverage(
|
|
82
|
+
[
|
|
83
|
+
[0, 4],
|
|
84
|
+
[1, 3],
|
|
85
|
+
[2, 6],
|
|
86
|
+
[4, 8],
|
|
87
|
+
[8, 10],
|
|
88
|
+
[11, 14],
|
|
89
|
+
[11, 13],
|
|
90
|
+
[11, 12],
|
|
91
|
+
[15, 18],
|
|
92
|
+
[16, 18],
|
|
93
|
+
[17, 18],
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
[0, 1, 1],
|
|
97
|
+
[1, 2, 2],
|
|
98
|
+
[2, 3, 3],
|
|
99
|
+
[3, 6, 2],
|
|
100
|
+
[6, 10, 1],
|
|
101
|
+
[11, 12, 3],
|
|
102
|
+
[12, 13, 2],
|
|
103
|
+
[13, 14, 1],
|
|
104
|
+
[15, 16, 1],
|
|
105
|
+
[16, 17, 2],
|
|
106
|
+
[17, 18, 3],
|
|
107
|
+
]
|
|
108
|
+
));
|
|
87
109
|
|
|
88
|
-
test("
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
test("Multiple identical overlapping segments", () =>
|
|
111
|
+
testSimpleCoverage(
|
|
112
|
+
[
|
|
113
|
+
[1, 2],
|
|
114
|
+
[3, 4],
|
|
115
|
+
[3, 4],
|
|
116
|
+
[5, 6],
|
|
117
|
+
[5, 6],
|
|
118
|
+
[5, 6],
|
|
119
|
+
],
|
|
120
|
+
[
|
|
121
|
+
[1, 2, 1],
|
|
122
|
+
[3, 4, 2],
|
|
123
|
+
[5, 6, 3],
|
|
124
|
+
]
|
|
125
|
+
));
|
|
99
126
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
test("Adjacent segments with equal coverage are merged", () =>
|
|
128
|
+
testSimpleCoverage(
|
|
129
|
+
[
|
|
130
|
+
[1, 2],
|
|
131
|
+
[2, 3],
|
|
132
|
+
[3, 4],
|
|
133
|
+
[5, 6],
|
|
134
|
+
[6, 7],
|
|
135
|
+
[7, 8],
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
[1, 4, 1],
|
|
139
|
+
[5, 8, 1],
|
|
140
|
+
]
|
|
141
|
+
));
|
|
112
142
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
143
|
+
test("Chromosomes pass through", () => {
|
|
144
|
+
const reads = [
|
|
145
|
+
{ chrom: "chr1", start: 0, end: 1 },
|
|
146
|
+
{ chrom: "chr2", start: 0, end: 1 },
|
|
147
|
+
{ chrom: "chr3", start: 1, end: 3 },
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
const coverageSegments = [
|
|
151
|
+
{ chrom: "chr1", start: 0, end: 1, coverage: 1 },
|
|
152
|
+
{ chrom: "chr2", start: 0, end: 1, coverage: 1 },
|
|
153
|
+
{ chrom: "chr3", start: 1, end: 3, coverage: 1 },
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
/** @type {CoverageParams} */
|
|
157
|
+
const coverageConfig = {
|
|
158
|
+
type: "coverage",
|
|
159
|
+
chrom: "chrom",
|
|
160
|
+
start: "start",
|
|
161
|
+
end: "end",
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
expect(transform(coverageConfig, reads)).toEqual(coverageSegments);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("Typical weighted coverage", () =>
|
|
168
|
+
testWeightedCoverage(
|
|
169
|
+
[
|
|
170
|
+
[0, 4, 1],
|
|
171
|
+
[1, 3, 2],
|
|
172
|
+
[2, 6, 3],
|
|
173
|
+
[8, 10, -1],
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
[0, 1, 1],
|
|
177
|
+
[1, 2, 3],
|
|
178
|
+
[2, 3, 6],
|
|
179
|
+
[3, 4, 4],
|
|
180
|
+
[4, 6, 3],
|
|
181
|
+
[8, 10, -1],
|
|
182
|
+
]
|
|
183
|
+
));
|
|
184
|
+
|
|
185
|
+
test("Multiple weights at a single locus", () =>
|
|
186
|
+
testWeightedCoverage(
|
|
187
|
+
[
|
|
188
|
+
// -- Locus 1
|
|
189
|
+
[1, 2, 1],
|
|
190
|
+
[1, 2, 2],
|
|
191
|
+
[1, 2, 3],
|
|
192
|
+
[1, 2, 4],
|
|
193
|
+
[1, 2, 5],
|
|
194
|
+
// -- Locus 2
|
|
195
|
+
[2, 3, 2],
|
|
196
|
+
[2, 3, 3],
|
|
197
|
+
[2, 3, 4],
|
|
198
|
+
[2, 3, 5],
|
|
199
|
+
[2, 3, 6],
|
|
200
|
+
],
|
|
201
|
+
[
|
|
202
|
+
// -- Locus 1
|
|
203
|
+
[1, 2, 15],
|
|
204
|
+
// -- Locus 2
|
|
205
|
+
[2, 3, 20],
|
|
206
|
+
]
|
|
207
|
+
));
|
|
121
208
|
|
|
122
|
-
|
|
209
|
+
test("Adjacent segments with different weights produce separated segments", () =>
|
|
210
|
+
testWeightedCoverage(
|
|
211
|
+
[
|
|
212
|
+
// -- Cluster 1
|
|
213
|
+
[1, 2, 2],
|
|
214
|
+
[2, 3, 1],
|
|
215
|
+
[3, 4, 1],
|
|
216
|
+
// -- Cluster 2
|
|
217
|
+
[5, 6, 1],
|
|
218
|
+
[6, 7, 2],
|
|
219
|
+
[7, 8, 1],
|
|
220
|
+
// -- Cluster 3
|
|
221
|
+
[9, 10, 1],
|
|
222
|
+
[10, 11, 1],
|
|
223
|
+
[11, 12, 2],
|
|
224
|
+
],
|
|
225
|
+
[
|
|
226
|
+
// -- Cluster 1
|
|
227
|
+
[1, 2, 2],
|
|
228
|
+
[2, 4, 1],
|
|
229
|
+
// -- Cluster 2
|
|
230
|
+
[5, 6, 1],
|
|
231
|
+
[6, 7, 2],
|
|
232
|
+
[7, 8, 1],
|
|
233
|
+
// -- Cluster 3
|
|
234
|
+
[9, 11, 1],
|
|
235
|
+
[11, 12, 2],
|
|
236
|
+
]
|
|
237
|
+
));
|
|
123
238
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Merges multiple sorted arrays.
|
|
3
3
|
*
|
|
4
4
|
* @param {T[][]} arrays
|
|
5
|
+
* @param {function(T):void} handler a function that will be called for each element
|
|
5
6
|
* @param {function(T):number} [accessor]
|
|
6
7
|
* @template T
|
|
7
8
|
*/
|
|
8
|
-
export default function kWayMerge<T>(arrays: T[][], accessor?: (arg0: T) => number):
|
|
9
|
+
export default function kWayMerge<T>(arrays: T[][], handler: (arg0: T) => void, accessor?: (arg0: T) => number): void;
|
|
9
10
|
//# sourceMappingURL=kWayMerge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kWayMerge.d.ts","sourceRoot":"","sources":["../../../src/utils/kWayMerge.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"kWayMerge.d.ts","sourceRoot":"","sources":["../../../src/utils/kWayMerge.js"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,0EAJuB,IAAI,0BACJ,MAAM,QAmC5B"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import FlatQueue from "flatqueue";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Merges multiple sorted arrays.
|
|
5
5
|
*
|
|
6
6
|
* @param {T[][]} arrays
|
|
7
|
+
* @param {function(T):void} handler a function that will be called for each element
|
|
7
8
|
* @param {function(T):number} [accessor]
|
|
8
9
|
* @template T
|
|
9
10
|
*/
|
|
10
|
-
export default function
|
|
11
|
+
export default function kWayMerge(arrays, handler, accessor = (x) => +x) {
|
|
11
12
|
// https://www.wikiwand.com/en/K-way_merge_algorithm
|
|
12
13
|
|
|
13
14
|
// This could be optimized by implementing a tournament tree or
|
|
@@ -31,7 +32,7 @@ export default function* kWayMerge(arrays, accessor = (x) => +x) {
|
|
|
31
32
|
let pointer = pointers[i];
|
|
32
33
|
const element = array[pointer++];
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
handler(element);
|
|
35
36
|
|
|
36
37
|
if (pointer < array.length) {
|
|
37
38
|
const newValue = accessor(array[pointer]);
|
|
@@ -22,5 +22,9 @@ test("k-way merge merges multiple sorted arrays", () => {
|
|
|
22
22
|
/** @type {function(any):number} */
|
|
23
23
|
const accessor = (d) => d.a;
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
/** @type {{a: number}[]} */
|
|
26
|
+
const result = [];
|
|
27
|
+
kWayMerge(arrays, (d) => result.push(d), accessor);
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual(sorted);
|
|
26
30
|
});
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"contributors": [],
|
|
9
9
|
"license": "MIT",
|
|
10
|
-
"version": "0.43.
|
|
10
|
+
"version": "0.43.2",
|
|
11
11
|
"jsdelivr": "dist/bundle/index.js",
|
|
12
12
|
"unpkg": "dist/bundle/index.js",
|
|
13
13
|
"browser": "dist/bundle/index.js",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"vega-scale": "^7.1.1",
|
|
66
66
|
"vega-util": "^1.16.0"
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "12ff70326672ed7ca34ef081c01fc25f19cf1ede"
|
|
69
69
|
}
|