Skip to content

Commit 48da090

Browse files
authored
feat: add xyFromInterleaved and xyToInterleaved (#371)
* feat: add xyFromInterleaved and xyToInterleaved Convert between flat [x,y,x,y,...] arrays and DataXY objects. * chore: update exports snapshot and add .claude to .gitignore * chore: fix eslint * chore: fix eslint * fix: suppress TS errors where x.at(-1) is guaranteed non-empty * feat: return Float64Array from xyFromInterleaved and xyToInterleaved * chore: fix eslint * feat: throw RangeError when interleaved data length is odd
1 parent 904d97c commit 48da090

12 files changed

Lines changed: 108 additions & 14 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ dist
4444

4545
.DS_Store
4646
.eslintcache
47+
.claude

src/__tests__/__snapshots__/index.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ exports[`existence of exported functions 1`] = `
9191
"xyEquallySpaced",
9292
"xyExtract",
9393
"xyFilter",
94+
"xyFromInterleaved",
9495
"xyFilterMinYValue",
9596
"xyFilterTopYValues",
9697
"xyFilterX",
@@ -124,6 +125,7 @@ exports[`existence of exported functions 1`] = `
124125
"xyRollingCircleTransform",
125126
"xySetYValue",
126127
"xySortX",
128+
"xyToInterleaved",
127129
"xyToXYArray",
128130
"xyToXYObject",
129131
"xyUniqueX",

src/matrix/matrixPQN.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function matrixPQN(
3232
medianOfQuotients: number[];
3333
} {
3434
const { max = 100 } = options;
35-
const matrixB = new Matrix(matrix as number[][]);
35+
const matrixB = new Matrix(matrix);
3636
for (let i = 0; i < matrixB.rows; i++) {
3737
const normalizationFactor = matrixB.getRowVector(i).norm('frobenius') / max;
3838
const row = matrixB.getRowVector(i).div(normalizationFactor);

src/utils/__tests__/calculateAdaptiveWeights.test.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,7 @@ test('works with different array types', () => {
134134
const baseline = [1.1, 2.1, 3.1, 4.1, 5.1];
135135
const weights = [1, 1, 1, 1, 1];
136136

137-
const result = calculateAdaptiveWeights(
138-
yData as any,
139-
baseline as any,
140-
weights as any,
141-
{},
142-
);
137+
const result = calculateAdaptiveWeights(yData, baseline, weights, {});
143138

144139
expect(result).toBeDefined();
145140
expect(result[0]).toBe(1);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { expect, test } from 'vitest';
2+
3+
import { xyFromInterleaved } from '../index.ts';
4+
5+
test('basic', () => {
6+
expect(xyFromInterleaved([1, 10, 2, 20, 3, 30])).toStrictEqual({
7+
x: new Float64Array([1, 2, 3]),
8+
y: new Float64Array([10, 20, 30]),
9+
});
10+
});
11+
12+
test('Float64Array input', () => {
13+
expect(xyFromInterleaved(new Float64Array([1, 10, 2, 20]))).toStrictEqual({
14+
x: new Float64Array([1, 2]),
15+
y: new Float64Array([10, 20]),
16+
});
17+
});
18+
19+
test('empty array', () => {
20+
expect(xyFromInterleaved([])).toStrictEqual({
21+
x: new Float64Array(0),
22+
y: new Float64Array(0),
23+
});
24+
});
25+
26+
test('odd length throws', () => {
27+
expect(() => xyFromInterleaved([1, 2, 3])).toThrow(
28+
/data length must be even/,
29+
);
30+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect, test } from 'vitest';
2+
3+
import { xyFromInterleaved, xyToInterleaved } from '../index.ts';
4+
5+
test('basic', () => {
6+
expect(xyToInterleaved({ x: [1, 2, 3], y: [10, 20, 30] })).toStrictEqual(
7+
new Float64Array([1, 10, 2, 20, 3, 30]),
8+
);
9+
});
10+
11+
test('empty', () => {
12+
expect(xyToInterleaved({ x: [], y: [] })).toStrictEqual(new Float64Array(0));
13+
});
14+
15+
test('round-trip with xyFromInterleaved', () => {
16+
const interleaved = new Float64Array([1, 10, 2, 20, 3, 30]);
17+
18+
expect(xyToInterleaved(xyFromInterleaved(interleaved))).toStrictEqual(
19+
interleaved,
20+
);
21+
});

src/xy/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './xyEnsureGrowingX.ts';
77
export * from './xyEquallySpaced.ts';
88
export * from './xyExtract.ts';
99
export * from './xyFilter.ts';
10+
export * from './xyFromInterleaved.ts';
1011
export * from './xyFilterMinYValue.ts';
1112
export * from './xyFilterTopYValues.ts';
1213
export * from './xyFilterX.ts';
@@ -41,6 +42,7 @@ export * from './xyRolling.ts';
4142
export * from './xyRollingCircleTransform.ts';
4243
export * from './xySetYValue.ts';
4344
export * from './xySortX.ts';
45+
export * from './xyToInterleaved.ts';
4446
export * from './xyToXYArray.ts';
4547
export * from './xyToXYObject.ts';
4648
export * from './xyUniqueX.ts';

src/xy/xyFilterX.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ export function xyFilterX(
4747
}
4848
const {
4949
from = x[0],
50-
to = x.at(-1) as number,
50+
to = x.at(-1),
5151
zones = [{ from, to }],
5252
exclusions = [],
5353
} = options;
5454

55+
// @ts-expect-error -- x.at(-1) returns number | undefined but array is guaranteed non-empty here
5556
const normalizedZones = zonesNormalize(zones, { from, to, exclusions });
5657

5758
let currentZoneIndex = 0;

src/xy/xyFromInterleaved.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { DataXY } from 'cheminfo-types';
2+
3+
/**
4+
* Convert a flat interleaved array [x, y, x, y, ...] to a DataXY object.
5+
* @param data - Flat array alternating x and y values.
6+
* @returns DataXY object with separate x and y arrays.
7+
*/
8+
export function xyFromInterleaved(
9+
data: number[] | Float64Array,
10+
): DataXY<Float64Array> {
11+
if (data.length % 2 !== 0) {
12+
throw new RangeError(
13+
`xyFromInterleaved: data length must be even, got ${data.length}`,
14+
);
15+
}
16+
const length = data.length / 2;
17+
const x = new Float64Array(length);
18+
const y = new Float64Array(length);
19+
for (let i = 0; i < length; i++) {
20+
x[i] = data[2 * i];
21+
y[i] = data[2 * i + 1];
22+
}
23+
return { x, y };
24+
}

src/xy/xyReduce.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,14 @@ export function xyReduce(
6969
const { x, y } = data;
7070
const {
7171
from = x[0],
72-
to = x.at(-1) as number,
72+
to = x.at(-1),
7373
nbPoints = 4001,
7474
optimize = false,
7575
} = options;
7676
let { zones = [] } = options;
7777

7878
zones = zonesNormalize(zones, { from, to });
79+
// @ts-expect-error -- x.at(-1) returns number | undefined but array is guaranteed non-empty here
7980
if (zones.length === 0) zones = [{ from, to }]; // we take everything
8081

8182
const { internalZones, totalPoints } = getInternalZones(zones, x);

0 commit comments

Comments
 (0)