feat(frontend): add extractNumber helper

This commit is contained in:
Experiments DB Dev
2026-05-01 07:50:56 -04:00
parent 6a6e6b310a
commit 0b83795665
2 changed files with 42 additions and 0 deletions
+12
View File
@@ -218,3 +218,15 @@ export function runAnalysis(rows, timestampCol, noteCol, classifications) {
consecutiveStats,
};
}
// ── Numeric extraction (for numerical buckets) ────────────────────────────────
/**
* Extract the first signed decimal from a string. Returns null if none found.
* Examples: "L25.5" -> 25.5, "R-12" -> -12, "abc" -> null
*/
export function extractNumber(str) {
if (str == null) return null;
const m = String(str).match(/-?\d+(\.\d+)?/);
return m ? parseFloat(m[0]) : null;
}
+30
View File
@@ -3,6 +3,7 @@ import {
parseCSV,
getUniqueNoteValues,
runAnalysis,
extractNumber,
} from '../src/lib/csvAnalysis';
// ── Fixtures ───────────────────────────────────────────────────────────────────
@@ -276,3 +277,32 @@ describe('runAnalysis — edge cases', () => {
expect(result.consecutiveStats['Failure'].totalRuns).toBe(1);
});
});
describe('extractNumber', () => {
test('extracts positive decimal from prefixed string', () => {
expect(extractNumber('L25.5')).toBe(25.5);
});
test('extracts negative integer with letter prefix', () => {
expect(extractNumber('R-12')).toBe(-12);
});
test('extracts integer from underscore-separated label', () => {
expect(extractNumber('trial_007')).toBe(7);
});
test('does not parse scientific notation as one number', () => {
// Accepted limitation: regex matches "3.2", strips "e2"
expect(extractNumber('3.2e2')).toBe(3.2);
});
test('strips trailing units', () => {
expect(extractNumber('42 ms')).toBe(42);
});
test('returns null for purely alphabetic string', () => {
expect(extractNumber('abc')).toBeNull();
});
test('returns null for empty string', () => {
expect(extractNumber('')).toBeNull();
});
test('returns null for null/undefined', () => {
expect(extractNumber(null)).toBeNull();
expect(extractNumber(undefined)).toBeNull();
});
});