@genome-spy/core 0.30.0 → 0.30.3

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.
Files changed (234) hide show
  1. package/dist/index.es.js +16379 -0
  2. package/dist/index.js +43 -43
  3. package/package.json +10 -7
  4. package/src/data/collector.js +0 -183
  5. package/src/data/collector.test.js +0 -84
  6. package/src/data/dataFlow.js +0 -148
  7. package/src/data/dataFlow.test.js +0 -5
  8. package/src/data/facetNode.js +0 -17
  9. package/src/data/flow.test.js +0 -72
  10. package/src/data/flowBatch.d.ts +0 -40
  11. package/src/data/flowNode.js +0 -283
  12. package/src/data/flowNode.test.js +0 -50
  13. package/src/data/flowOptimizer.js +0 -123
  14. package/src/data/flowOptimizer.test.js +0 -193
  15. package/src/data/flowTestUtils.js +0 -63
  16. package/src/data/formats/fasta.js +0 -32
  17. package/src/data/formats/fasta.test.js +0 -27
  18. package/src/data/sources/dataSource.js +0 -22
  19. package/src/data/sources/dataSourceFactory.js +0 -24
  20. package/src/data/sources/dataUtils.js +0 -78
  21. package/src/data/sources/dynamicCallbackSource.js +0 -57
  22. package/src/data/sources/dynamicSource.js +0 -37
  23. package/src/data/sources/inlineSource.js +0 -67
  24. package/src/data/sources/inlineSource.test.js +0 -56
  25. package/src/data/sources/namedSource.js +0 -79
  26. package/src/data/sources/sequenceSource.js +0 -46
  27. package/src/data/sources/sequenceSource.test.js +0 -46
  28. package/src/data/sources/urlSource.js +0 -74
  29. package/src/data/transforms/aggregate.js +0 -70
  30. package/src/data/transforms/clone.js +0 -40
  31. package/src/data/transforms/clone.test.js +0 -11
  32. package/src/data/transforms/coverage.js +0 -187
  33. package/src/data/transforms/coverage.test.js +0 -123
  34. package/src/data/transforms/filter.js +0 -37
  35. package/src/data/transforms/filter.test.js +0 -18
  36. package/src/data/transforms/filterScoredLabels.js +0 -134
  37. package/src/data/transforms/flattenCompressedExons.js +0 -57
  38. package/src/data/transforms/flattenDelimited.js +0 -74
  39. package/src/data/transforms/flattenDelimited.test.js +0 -87
  40. package/src/data/transforms/flattenSequence.js +0 -39
  41. package/src/data/transforms/flattenSequence.test.js +0 -34
  42. package/src/data/transforms/formula.js +0 -39
  43. package/src/data/transforms/formula.test.js +0 -19
  44. package/src/data/transforms/identifier.js +0 -108
  45. package/src/data/transforms/identifier.test.js +0 -83
  46. package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
  47. package/src/data/transforms/measureText.js +0 -44
  48. package/src/data/transforms/pileup.js +0 -128
  49. package/src/data/transforms/pileup.test.js +0 -70
  50. package/src/data/transforms/project.js +0 -41
  51. package/src/data/transforms/project.test.js +0 -32
  52. package/src/data/transforms/regexExtract.js +0 -61
  53. package/src/data/transforms/regexExtract.test.js +0 -67
  54. package/src/data/transforms/regexFold.js +0 -141
  55. package/src/data/transforms/regexFold.test.js +0 -160
  56. package/src/data/transforms/sample.js +0 -101
  57. package/src/data/transforms/sample.test.js +0 -38
  58. package/src/data/transforms/stack.js +0 -137
  59. package/src/data/transforms/stack.test.js +0 -91
  60. package/src/data/transforms/transformFactory.js +0 -60
  61. package/src/embedApi.d.ts +0 -67
  62. package/src/encoder/accessor.js +0 -82
  63. package/src/encoder/accessor.test.js +0 -47
  64. package/src/encoder/encoder.js +0 -394
  65. package/src/encoder/encoder.test.js +0 -98
  66. package/src/fonts/Lato-Regular.json +0 -1267
  67. package/src/fonts/Lato-Regular.png +0 -0
  68. package/src/fonts/OFL.txt +0 -93
  69. package/src/fonts/README.md +0 -3
  70. package/src/fonts/bmFont.d.ts +0 -58
  71. package/src/fonts/bmFontManager.js +0 -357
  72. package/src/fonts/bmFontMetrics.js +0 -108
  73. package/src/genome/genome.js +0 -317
  74. package/src/genome/genome.test.js +0 -188
  75. package/src/genome/genomeStore.js +0 -54
  76. package/src/genome/locusFormat.js +0 -31
  77. package/src/genome/scaleIndex.d.ts +0 -38
  78. package/src/genome/scaleIndex.js +0 -166
  79. package/src/genome/scaleIndex.test.js +0 -78
  80. package/src/genome/scaleLocus.d.ts +0 -11
  81. package/src/genome/scaleLocus.js +0 -108
  82. package/src/genome/scaleLocus.test.js +0 -4
  83. package/src/genomeSpy.js +0 -785
  84. package/src/gl/arrayBuilder.js +0 -199
  85. package/src/gl/dataToVertices.js +0 -636
  86. package/src/gl/includes/common.glsl +0 -63
  87. package/src/gl/includes/picking.fragment.glsl +0 -1
  88. package/src/gl/includes/picking.vertex.glsl +0 -27
  89. package/src/gl/includes/sampleFacet.glsl +0 -107
  90. package/src/gl/includes/scales.glsl +0 -112
  91. package/src/gl/link.fragment.glsl +0 -18
  92. package/src/gl/link.vertex.glsl +0 -111
  93. package/src/gl/point.fragment.glsl +0 -123
  94. package/src/gl/point.vertex.glsl +0 -129
  95. package/src/gl/rect.fragment.glsl +0 -51
  96. package/src/gl/rect.vertex.glsl +0 -114
  97. package/src/gl/rule.fragment.glsl +0 -52
  98. package/src/gl/rule.vertex.glsl +0 -89
  99. package/src/gl/text.fragment.glsl +0 -31
  100. package/src/gl/text.vertex.glsl +0 -246
  101. package/src/gl/webGLHelper.js +0 -504
  102. package/src/img/bowtie.svg +0 -1
  103. package/src/img/genomespy-favicon.svg +0 -34
  104. package/src/index.html +0 -11
  105. package/src/index.js +0 -128
  106. package/src/marks/link.js +0 -175
  107. package/src/marks/mark.js +0 -975
  108. package/src/marks/markUtils.js +0 -125
  109. package/src/marks/pointMark.js +0 -251
  110. package/src/marks/rectMark.js +0 -241
  111. package/src/marks/rule.js +0 -250
  112. package/src/marks/text.js +0 -278
  113. package/src/node_modules/.vitest/results.json +0 -1
  114. package/src/scale/colorUtils.js +0 -184
  115. package/src/scale/glslScaleGenerator.js +0 -502
  116. package/src/scale/scale.js +0 -451
  117. package/src/scale/scale.test.js +0 -324
  118. package/src/scale/ticks.js +0 -203
  119. package/src/scale/ticks.test.js +0 -40
  120. package/src/singlePageApp.js +0 -13
  121. package/src/spec/axis.d.ts +0 -296
  122. package/src/spec/channel.d.ts +0 -430
  123. package/src/spec/data.d.ts +0 -196
  124. package/src/spec/font.d.ts +0 -15
  125. package/src/spec/genome.d.ts +0 -35
  126. package/src/spec/mark.d.ts +0 -429
  127. package/src/spec/root.d.ts +0 -17
  128. package/src/spec/sampleView.d.ts +0 -180
  129. package/src/spec/scale.d.ts +0 -273
  130. package/src/spec/title.d.ts +0 -102
  131. package/src/spec/tooltip.d.ts +0 -9
  132. package/src/spec/transform.d.ts +0 -479
  133. package/src/spec/view.d.ts +0 -201
  134. package/src/styles/genome-spy.scss +0 -153
  135. package/src/tooltip/dataTooltipHandler.js +0 -64
  136. package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
  137. package/src/tooltip/tooltipHandler.ts +0 -12
  138. package/src/types/filetypes.d.ts +0 -14
  139. package/src/types/flatqueue.d.ts +0 -53
  140. package/src/types/glsl.d.ts +0 -4
  141. package/src/types/internmap.d.ts +0 -22
  142. package/src/types/object.d.ts +0 -21
  143. package/src/types/vega-loader.d.ts +0 -1
  144. package/src/types/vega-scale.d.ts +0 -60
  145. package/src/utils/addBaseUrl.js +0 -19
  146. package/src/utils/addBaseUrl.test.js +0 -22
  147. package/src/utils/animator.js +0 -83
  148. package/src/utils/arrayUtils.js +0 -61
  149. package/src/utils/binnedIndex.js +0 -167
  150. package/src/utils/binnedIndex.test.js +0 -155
  151. package/src/utils/clamp.js +0 -8
  152. package/src/utils/cloner.js +0 -34
  153. package/src/utils/cloner.test.js +0 -24
  154. package/src/utils/coalesce.js +0 -11
  155. package/src/utils/coalesce.test.js +0 -16
  156. package/src/utils/concatIterables.js +0 -26
  157. package/src/utils/concatIterables.test.js +0 -8
  158. package/src/utils/debounce.js +0 -37
  159. package/src/utils/domainArray.js +0 -216
  160. package/src/utils/domainArray.test.js +0 -130
  161. package/src/utils/eerp.js +0 -13
  162. package/src/utils/expression.js +0 -32
  163. package/src/utils/field.js +0 -28
  164. package/src/utils/formatObject.js +0 -31
  165. package/src/utils/indexer.js +0 -43
  166. package/src/utils/indexer.test.js +0 -47
  167. package/src/utils/inertia.js +0 -124
  168. package/src/utils/interactionEvent.js +0 -33
  169. package/src/utils/iterateNestedMaps.js +0 -21
  170. package/src/utils/iterateNestedMaps.test.js +0 -33
  171. package/src/utils/kWayMerge.js +0 -42
  172. package/src/utils/kWayMerge.test.js +0 -26
  173. package/src/utils/layout/flexLayout.js +0 -368
  174. package/src/utils/layout/flexLayout.test.js +0 -311
  175. package/src/utils/layout/grid.js +0 -95
  176. package/src/utils/layout/grid.test.js +0 -71
  177. package/src/utils/layout/padding.js +0 -120
  178. package/src/utils/layout/point.js +0 -23
  179. package/src/utils/layout/rectangle.js +0 -288
  180. package/src/utils/layout/rectangle.test.js +0 -172
  181. package/src/utils/mergeObjects.js +0 -99
  182. package/src/utils/mergeObjects.test.js +0 -42
  183. package/src/utils/numberExtractor.js +0 -24
  184. package/src/utils/numberExtractor.test.js +0 -6
  185. package/src/utils/point.js +0 -14
  186. package/src/utils/propertyCacher.js +0 -70
  187. package/src/utils/propertyCacher.test.js +0 -85
  188. package/src/utils/propertyCoalescer.js +0 -42
  189. package/src/utils/propertyCoalescer.test.js +0 -22
  190. package/src/utils/reservationMap.js +0 -103
  191. package/src/utils/reservationMap.test.js +0 -20
  192. package/src/utils/scaleNull.js +0 -19
  193. package/src/utils/setOperations.js +0 -75
  194. package/src/utils/smoothstep.js +0 -10
  195. package/src/utils/throttle.js +0 -34
  196. package/src/utils/topK.js +0 -76
  197. package/src/utils/topK.test.js +0 -64
  198. package/src/utils/transition.js +0 -74
  199. package/src/utils/ui/tooltip.js +0 -189
  200. package/src/utils/url.js +0 -22
  201. package/src/utils/variableTools.js +0 -24
  202. package/src/utils/variableTools.test.js +0 -13
  203. package/src/view/axisResolution.js +0 -140
  204. package/src/view/axisResolution.test.js +0 -201
  205. package/src/view/axisView.js +0 -747
  206. package/src/view/concatView.js +0 -45
  207. package/src/view/containerView.js +0 -159
  208. package/src/view/facetView.js +0 -491
  209. package/src/view/flowBuilder.js +0 -367
  210. package/src/view/flowBuilder.test.js +0 -125
  211. package/src/view/gridView.js +0 -786
  212. package/src/view/implicitRootView.js +0 -14
  213. package/src/view/importView.js +0 -19
  214. package/src/view/layerView.js +0 -74
  215. package/src/view/rendering.d.ts +0 -44
  216. package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
  217. package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
  218. package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
  219. package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
  220. package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
  221. package/src/view/renderingContext/viewRenderingContext.js +0 -41
  222. package/src/view/scaleResolution.js +0 -797
  223. package/src/view/scaleResolution.test.js +0 -572
  224. package/src/view/scaleResolutionApi.d.ts +0 -40
  225. package/src/view/testUtils.js +0 -51
  226. package/src/view/title.js +0 -165
  227. package/src/view/unitView.js +0 -382
  228. package/src/view/view.js +0 -612
  229. package/src/view/view.test.js +0 -214
  230. package/src/view/viewContext.d.ts +0 -62
  231. package/src/view/viewFactory.js +0 -181
  232. package/src/view/viewFactory.test.js +0 -17
  233. package/src/view/viewUtils.js +0 -327
  234. package/src/view/zoom.js +0 -89
@@ -1,128 +0,0 @@
1
- /* eslint-disable no-unmodified-loop-condition */
2
- import FlatQueue from "flatqueue";
3
- import { isNumber } from "vega-util";
4
- import { field } from "../../utils/field";
5
- import FlowNode, { BEHAVIOR_MODIFIES } from "../flowNode";
6
-
7
- const maxDepth = 65536;
8
-
9
- /**
10
- * @typedef {import("../../spec/transform").PileupParams} PileupParams
11
- */
12
- export default class PileupTransform extends FlowNode {
13
- get behavior() {
14
- return BEHAVIOR_MODIFIES;
15
- }
16
-
17
- /**
18
- *
19
- * @param {PileupParams} params
20
- */
21
- constructor(params) {
22
- super();
23
-
24
- this.params = params;
25
- }
26
-
27
- reset() {
28
- this.initialize();
29
- }
30
-
31
- initialize() {
32
- const params = this.params;
33
-
34
- const laneField = params.as || "lane";
35
- const spacing = isNumber(params.spacing) ? params.spacing : 1;
36
- const startAccessor = field(params.start);
37
- const endAccessor = field(params.end);
38
-
39
- // We choose the implementation based on the need of order preference.
40
- // The preference-aware algorithm has a lousy O(n^2) time complexity but
41
- // it's acceptable for finding lanes for genes based on their strands.
42
-
43
- // Both implementations expect the items to be sorted by their start
44
- // coordinates.
45
-
46
- if (!params.preference !== !params.preferredOrder) {
47
- throw new Error(
48
- `Must specify both "preference" and "preferredOrder"`
49
- );
50
- } else if (params.preference) {
51
- const freeLaneMap = new Float64Array(maxDepth);
52
-
53
- const preferenceAccessor = field(params.preference);
54
- /** @type {any[]} */
55
- const preferredOrder = params.preferredOrder;
56
-
57
- let lastStart = Infinity;
58
-
59
- /** @param {Record<string, any>} datum */
60
- this.handle = (datum) => {
61
- const start = startAccessor(datum);
62
- if (start < lastStart) {
63
- // Reset if encountered a new chromosome...
64
- freeLaneMap.fill(-Infinity);
65
- }
66
- lastStart = start;
67
-
68
- // Linear search, but the number of preferences is likely be low
69
- const preferredLane = preferredOrder.indexOf(
70
- preferenceAccessor(datum)
71
- );
72
- let lane = -1;
73
- if (preferredLane >= 0 && freeLaneMap[preferredLane] < start) {
74
- lane = preferredLane;
75
- } else {
76
- const start = startAccessor(datum);
77
- for (lane = 0; lane < freeLaneMap.length; lane++) {
78
- if (freeLaneMap[lane] < start) {
79
- break;
80
- }
81
- }
82
- if (lane >= freeLaneMap.length) {
83
- throw new Error("Out of lanes!");
84
- }
85
- }
86
- freeLaneMap[lane] = endAccessor(datum) + spacing;
87
- datum[laneField] = lane;
88
- this._propagate(datum);
89
- };
90
- } else {
91
- /** @type {FlatQueue<number>} */
92
- const ends = new FlatQueue();
93
-
94
- /** @type {FlatQueue<number>} */
95
- const freeLanes = new FlatQueue();
96
-
97
- // Keep track of the last processed element. Flush the queues if the start
98
- // pos suddenly decreases. This happens when piling up consecutive chromosomes.
99
- let lastStart = -Infinity;
100
-
101
- let maxLane = 0;
102
-
103
- /** @param {Record<string, any>} datum */
104
- this.handle = (datum) => {
105
- const start = startAccessor(datum);
106
- while (
107
- ends.length &&
108
- (ends.peekValue() <= start || start < lastStart)
109
- ) {
110
- const freeLane = ends.pop();
111
- freeLanes.push(freeLane, freeLane);
112
- }
113
- lastStart = start;
114
-
115
- let lane = freeLanes.pop();
116
- if (lane === undefined) {
117
- lane = maxLane++;
118
- }
119
-
120
- datum[laneField] = lane;
121
-
122
- this._propagate(datum);
123
-
124
- ends.push(lane, endAccessor(datum) + spacing);
125
- };
126
- }
127
- }
128
- }
@@ -1,70 +0,0 @@
1
- import { expect, test } from "vitest";
2
- import PileupTransform from "./pileup";
3
- import { processData } from "../flowTestUtils";
4
-
5
- // TODO: Test for lane preferences
6
-
7
- /**
8
- * @typedef {import("../../spec/transform").PileupParams} PileupParams
9
- */
10
-
11
- const reads = [
12
- [0, 4],
13
- [1, 3],
14
- [2, 6],
15
- [4, 8],
16
- [8, 10],
17
- [11, 14],
18
- [11, 13],
19
- [11, 12],
20
- [15, 18],
21
- [16, 18],
22
- [17, 18],
23
- ].map((d) => ({
24
- start: d[0],
25
- end: d[1],
26
- }));
27
-
28
- const lanes = [0, 1, 2, 1, 0, 0, 1, 2, 0, 1, 2];
29
-
30
- /** @type {PileupParams} */
31
- const params = {
32
- type: "pileup",
33
- start: "start",
34
- end: "end",
35
- };
36
-
37
- /**
38
- * @param {PileupParams} params
39
- * @param {any[]} data
40
- */
41
- function pileupTransform(params, data) {
42
- const t = new PileupTransform(params);
43
- t.initialize();
44
- return processData(t, data);
45
- }
46
-
47
- test("Pileup transform produces correct pileup", () => {
48
- const piledUp = lanes.map((d, i) => ({
49
- ...reads[i],
50
- lane: d,
51
- }));
52
-
53
- expect(pileupTransform(params, reads)).toEqual(piledUp);
54
- });
55
-
56
- test("Pileup transform produces correct pileup with consecutive contigs", () => {
57
- // Simulate data having multiple chromosomes, sorted by [chrom, pos].
58
- // Piling should handle suddenly decreasing start positions by freeing all
59
- // reserved lanes.
60
-
61
- const repeatedReads = [...reads, ...reads];
62
- const repeatedLanes = [...lanes, ...lanes];
63
-
64
- const piledUp = repeatedLanes.map((d, i) => ({
65
- ...repeatedReads[i],
66
- lane: d,
67
- }));
68
-
69
- expect(pileupTransform(params, repeatedReads)).toEqual(piledUp);
70
- });
@@ -1,41 +0,0 @@
1
- import { accessorName } from "vega-util";
2
- import { field } from "../../utils/field";
3
- import FlowNode, { BEHAVIOR_CLONES } from "../flowNode";
4
-
5
- /**
6
- * @typedef {import("../../spec/transform").ProjectParams} ProjectParams
7
- */
8
- export default class ProjectTransform extends FlowNode {
9
- get behavior() {
10
- return BEHAVIOR_CLONES;
11
- }
12
-
13
- /**
14
- *
15
- * @param {ProjectParams} params
16
- */
17
- constructor(params) {
18
- super();
19
-
20
- if (params.as && params.as.length != params.fields.length) {
21
- throw new Error(`"fields" and "as" have unequal lengths!`);
22
- }
23
-
24
- // TODO: "If unspecified, all fields will be copied using their existing names."
25
-
26
- const accessors = params.fields.map((f) => field(f));
27
- const as = params.as ? params.as : accessors.map(accessorName);
28
-
29
- /**
30
- * @param {any} datum
31
- */
32
- this.handle = (datum) => {
33
- /** @type {Record<string, any>} */
34
- const projected = {};
35
- for (let i = 0; i < accessors.length; i++) {
36
- projected[as[i]] = accessors[i](datum);
37
- }
38
- this._propagate(projected);
39
- };
40
- }
41
- }
@@ -1,32 +0,0 @@
1
- import { expect, test } from "vitest";
2
- import { processData } from "../flowTestUtils";
3
- import ProjectTransform from "./project";
4
-
5
- test("Project", () => {
6
- const data = [
7
- {
8
- foo: "FOO",
9
- bar: "BAR",
10
- baz: { a: "A" },
11
- },
12
- ];
13
-
14
- /** @param {import("./project").ProjectParams} params */
15
- const p = (params) => processData(new ProjectTransform(params), data);
16
-
17
- expect(p({ type: "project", fields: ["bar"] })).toEqual([{ bar: "BAR" }]);
18
-
19
- expect(
20
- p({ type: "project", fields: ["bar", "foo"], as: ["xBar", "xFoo"] })
21
- ).toEqual([{ xBar: "BAR", xFoo: "FOO" }]);
22
-
23
- expect(p({ type: "project", fields: ["baz.a"] })).toEqual([
24
- { "baz.a": "A" },
25
- ]);
26
-
27
- expect(p({ type: "project", fields: ["baz.a"], as: ["a"] })).toEqual([
28
- { a: "A" },
29
- ]);
30
-
31
- expect(() => p({ type: "project", fields: ["bar"], as: [] })).toThrow();
32
- });
@@ -1,61 +0,0 @@
1
- import { isString } from "vega-util";
2
- import { field } from "../../utils/field";
3
- import FlowNode, { BEHAVIOR_MODIFIES } from "../flowNode";
4
-
5
- /**
6
- * @typedef {import("../../spec/transform").RegexExtractParams} RegexExtractParams
7
- */
8
-
9
- export default class RegexExtractTransform extends FlowNode {
10
- get behavior() {
11
- return BEHAVIOR_MODIFIES;
12
- }
13
-
14
- /**
15
- * @param {RegexExtractParams} params
16
- */
17
- constructor(params) {
18
- super();
19
-
20
- const re = new RegExp(params.regex);
21
- const as = typeof params.as == "string" ? [params.as] : params.as;
22
- const accessor = field(params.field);
23
-
24
- /**
25
- *
26
- * @param {any} datum
27
- */
28
- this.handle = (datum) => {
29
- const value = accessor(datum);
30
- if (isString(value)) {
31
- const m = value.match(re);
32
-
33
- if (m) {
34
- if (m.length - 1 != as.length) {
35
- throw new Error(
36
- 'The number of RegEx groups and the length of "as" do not match!'
37
- );
38
- }
39
-
40
- for (let i = 0; i < as.length; i++) {
41
- datum[as[i]] = m[i + 1];
42
- }
43
- } else if (params.skipInvalidInput) {
44
- for (let i = 0; i < as.length; i++) {
45
- datum[as[i]] = undefined;
46
- }
47
- } else {
48
- throw new Error(
49
- `"${value}" does not match the given regex: ${re.toString()}`
50
- );
51
- }
52
- } else if (!params.skipInvalidInput) {
53
- throw new Error(
54
- `Trying to match a non-string field. Encountered type: ${typeof value}, field content: "${value}".`
55
- );
56
- }
57
-
58
- this._propagate(datum);
59
- };
60
- }
61
- }
@@ -1,67 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { processData } from "../flowTestUtils";
3
-
4
- import RegexExtractTransform from "./regexExtract";
5
-
6
- /**
7
- * @param {import("./regexExtract").RegexExtractParams} params
8
- * @param {any[]} data
9
- */
10
- function transform(params, data) {
11
- return processData(new RegexExtractTransform(params), data);
12
- }
13
-
14
- describe("RegexExtractTransform", () => {
15
- const rows = [{ a: "12-34" }, { a: "23-45" }];
16
-
17
- /** @type {import("./regexExtract").RegexExtractParams} */
18
- const params = {
19
- type: "regexExtract",
20
- regex: "^(\\d+)-(\\d+)$",
21
- field: "a",
22
- as: ["b", "c"],
23
- };
24
-
25
- test("Valid config and input", () => {
26
- expect(transform(params, rows)).toEqual([
27
- { a: "12-34", b: "12", c: "34" },
28
- { a: "23-45", b: "23", c: "45" },
29
- ]);
30
- });
31
-
32
- test("Invalid config", () => {
33
- /** @type {import("./regexExtract").RegexExtractParams} */
34
- const config2 = {
35
- type: "regexExtract",
36
- regex: "^(\\d+)-(\\d+)$",
37
- field: "a",
38
- as: ["b", "c", "d"],
39
- };
40
-
41
- expect(() => transform(config2, rows)).toThrow();
42
- });
43
-
44
- test("Invalid data", () => {
45
- const rows2 = [{ a: "12--34" }];
46
-
47
- expect(() => transform(params, rows2)).toThrow();
48
- });
49
-
50
- test("Invalid, non-string data", () => {
51
- const rows2 = [{ a: 123 }];
52
-
53
- expect(() => transform(params, rows2)).toThrow();
54
- });
55
-
56
- test("Skip invalid or non-string data", () => {
57
- const rows2 = [{ a: 123 }, { a: "xyzzy" }, { a: "12-34" }];
58
-
59
- expect(transform({ ...params, skipInvalidInput: true }, rows2)).toEqual(
60
- [
61
- { a: 123, b: undefined, c: undefined },
62
- { a: "xyzzy", b: undefined, c: undefined },
63
- { a: "12-34", b: "12", c: "34" },
64
- ]
65
- );
66
- });
67
- });
@@ -1,141 +0,0 @@
1
- import { asArray } from "../../utils/arrayUtils";
2
- import FlowNode, { BEHAVIOR_CLONES, isFileBatch } from "../flowNode";
3
-
4
- /**
5
- * Folds fields using a regex
6
- *
7
- * See: https://vega.github.io/vega/docs/transforms/fold/
8
- *
9
- * @typedef {import("../../spec/transform").RegexFoldParams} RegexFoldParams
10
- */
11
- export default class RegexFoldTransform extends FlowNode {
12
- get behavior() {
13
- return BEHAVIOR_CLONES;
14
- }
15
-
16
- /**
17
- * @param {RegexFoldParams} params
18
- */
19
- constructor(params) {
20
- super();
21
-
22
- const columnRegex = asArray(params.columnRegex).map(
23
- (re) => new RegExp(re)
24
- );
25
- // TODO: Consider using named groups
26
- const as = asArray(params.asValue);
27
-
28
- if (columnRegex.length != as.length) {
29
- throw new Error('Lengths of "columnRegex" and "as" are not equal!');
30
- }
31
-
32
- const skipRegex = params.skipRegex
33
- ? new RegExp(params.skipRegex)
34
- : undefined;
35
-
36
- const sampleKey = params.asKey || "sample";
37
-
38
- /** @type {[string, string[]][]} */
39
- let sampleAttrs;
40
-
41
- /** @type {string[]} */
42
- let includedColumns;
43
-
44
- /** @type {(datum: any, sampleId: string) => Record<string, any>} */
45
- let create;
46
-
47
- /**
48
- * @param {any} datum
49
- */
50
- const detectColumns = (datum) => {
51
- const colNames = /** @type {string[]} */ (Object.keys(datum));
52
-
53
- /** @type {Map<string, string[]>} */
54
- const sampleColMap = new Map();
55
-
56
- for (const [i, re] of columnRegex.entries()) {
57
- for (const colName of colNames) {
58
- const sampleId = re.exec(colName)?.[1];
59
- if (sampleId !== undefined) {
60
- let attrs = sampleColMap.get(sampleId);
61
- if (!attrs) {
62
- attrs = [];
63
- sampleColMap.set(sampleId, attrs);
64
- }
65
-
66
- attrs[i] = colName;
67
- }
68
- }
69
- }
70
-
71
- sampleAttrs = [...sampleColMap.entries()];
72
-
73
- includedColumns = colNames.filter(
74
- (colName) =>
75
- !columnRegex.some((re) => re.test(colName)) &&
76
- !(skipRegex && skipRegex.test(colName))
77
- );
78
-
79
- const props = [
80
- ...includedColumns.map(
81
- (prop) =>
82
- JSON.stringify(prop) +
83
- ": datum[" +
84
- JSON.stringify(prop) +
85
- "]"
86
- ),
87
- JSON.stringify(sampleKey) + ": sampleId",
88
- ...as.map((a) => JSON.stringify(a) + ": null"),
89
- ];
90
-
91
- // eslint-disable-next-line no-new-func
92
- create = /** @type {any} */ (
93
- new Function(
94
- "datum",
95
- "sampleId",
96
- "return {\n" + props.join(",\n") + "\n};"
97
- )
98
- );
99
- };
100
-
101
- /**
102
- * @param {any} datum
103
- */
104
- const doRegexFold = (datum) => {
105
- if (!sampleAttrs) {
106
- detectColumns(datum);
107
- }
108
-
109
- for (const [sampleId, attrs] of sampleAttrs) {
110
- const tidyRow = create(datum, sampleId);
111
- for (let i = 0; i < attrs.length; i++) {
112
- tidyRow[as[i]] = datum[attrs[i]];
113
- }
114
-
115
- this._propagate(tidyRow);
116
- }
117
- };
118
-
119
- /**
120
- * @param {any} datum
121
- */
122
- const detectAndHandle = (datum) => {
123
- detectColumns(datum);
124
- doRegexFold(datum);
125
- this.handle = doRegexFold;
126
- };
127
-
128
- this.handle = detectAndHandle;
129
-
130
- /**
131
- *
132
- * @param {import("../flowNode").FlowBatch} flowBatch
133
- */
134
- this.beginBatch = (flowBatch) => {
135
- if (isFileBatch(flowBatch)) {
136
- this.handle = detectAndHandle;
137
- }
138
- super.beginBatch(flowBatch);
139
- };
140
- }
141
- }
@@ -1,160 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { processData } from "../flowTestUtils";
3
- import RegexFoldTransform from "./regexFold";
4
-
5
- describe("RegexFold", () => {
6
- test("Transform single variable", () => {
7
- const sampleData = [
8
- {
9
- row: 1,
10
- sample1_a: "r1s1a",
11
- sample2_a: "r1s2a",
12
- },
13
- {
14
- row: 2,
15
- sample1_a: "r2s1a",
16
- sample2_a: "r2s2a",
17
- },
18
- ];
19
-
20
- /** @type { import("../../spec/transform").RegexFoldParams } */
21
- const singleGatherConfig = {
22
- type: "regexFold",
23
- columnRegex: "^(.*)_a$",
24
- asValue: "a",
25
- };
26
-
27
- const result = processData(
28
- new RegexFoldTransform(singleGatherConfig),
29
- sampleData
30
- );
31
-
32
- expect(result).toEqual([
33
- {
34
- row: 1,
35
- sample: "sample1",
36
- a: "r1s1a",
37
- },
38
- {
39
- row: 1,
40
- sample: "sample2",
41
- a: "r1s2a",
42
- },
43
- {
44
- row: 2,
45
- sample: "sample1",
46
- a: "r2s1a",
47
- },
48
- {
49
- row: 2,
50
- sample: "sample2",
51
- a: "r2s2a",
52
- },
53
- ]);
54
- });
55
-
56
- test("Transform single variable and skip specific columns", () => {
57
- const sampleData = [
58
- {
59
- row: 1,
60
- sample1_a: "r1s1a",
61
- sample2_a: "r1s2a",
62
- },
63
- {
64
- row: 2,
65
- sample1_a: "r2s1a",
66
- sample2_a: "r2s2a",
67
- },
68
- ];
69
-
70
- /** @type { import("../../spec/transform").RegexFoldParams } */
71
- const singleGatherConfig = {
72
- type: "regexFold",
73
- columnRegex: "^(.*)_a$",
74
- asValue: "a",
75
- skipRegex: "^row$",
76
- };
77
-
78
- const result = processData(
79
- new RegexFoldTransform(singleGatherConfig),
80
- sampleData
81
- );
82
-
83
- expect(result).toEqual([
84
- {
85
- sample: "sample1",
86
- a: "r1s1a",
87
- },
88
- {
89
- sample: "sample2",
90
- a: "r1s2a",
91
- },
92
- {
93
- sample: "sample1",
94
- a: "r2s1a",
95
- },
96
- {
97
- sample: "sample2",
98
- a: "r2s2a",
99
- },
100
- ]);
101
- });
102
-
103
- test("Transform multiple variables", () => {
104
- const sampleData = [
105
- {
106
- row: 1,
107
- sample1_a: "r1s1a",
108
- sample2_a: "r1s2a",
109
- sample1_b: "r1s1b",
110
- sample2_b: "r1s2b",
111
- },
112
- {
113
- row: 2,
114
- sample1_a: "r2s1a",
115
- sample2_a: "r2s2a",
116
- sample1_b: "r2s1b",
117
- sample2_b: "r2s2b",
118
- },
119
- ];
120
-
121
- /** @type { import("../../spec/transform").RegexFoldParams } */
122
- const singleGatherConfig = {
123
- type: "regexFold",
124
- columnRegex: ["^(.*)_a$", "^(.*)_b$"],
125
- asValue: ["a", "b"],
126
- };
127
-
128
- const result = processData(
129
- new RegexFoldTransform(singleGatherConfig),
130
- sampleData
131
- );
132
-
133
- expect(result).toEqual([
134
- {
135
- row: 1,
136
- sample: "sample1",
137
- a: "r1s1a",
138
- b: "r1s1b",
139
- },
140
- {
141
- row: 1,
142
- sample: "sample2",
143
- a: "r1s2a",
144
- b: "r1s2b",
145
- },
146
- {
147
- row: 2,
148
- sample: "sample1",
149
- a: "r2s1a",
150
- b: "r2s1b",
151
- },
152
- {
153
- row: 2,
154
- sample: "sample2",
155
- a: "r2s2a",
156
- b: "r2s2b",
157
- },
158
- ]);
159
- });
160
- });