From 54143679f5e4f138f9ccdd33f1999c0ca6d148d0 Mon Sep 17 00:00:00 2001 From: Janosh Riebesell Date: Fri, 19 Jul 2024 17:52:55 -0400 Subject: [PATCH] Add `parse_si_float` inverse function to `pretty_num` in `labels.ts` (#50) * add parse_si_float * add parse_si_float basically the inverse to pretty_num * test parse_si_float * update deps --- .pre-commit-config.yaml | 2 +- package.json | 30 ++++++++++++------------ src/lib/labels.ts | 31 +++++++++++++++++++++++++ tests/unit/labels.test.ts | 48 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d833663..df38264 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: exclude: ^changelog.md|.*/(structures|molecules)/.*\.json|.py$ - repo: https://github.com/pre-commit/mirrors-eslint - rev: v9.5.0 + rev: v9.7.0 hooks: - id: eslint types: [file] diff --git a/package.json b/package.json index 0aa7a00..388c8c8 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@iconify/svelte": "^4.0.2", - "@sveltejs/kit": "^2.5.17", + "@sveltejs/kit": "^2.5.18", "@threlte/core": "7.3.1", "d3": "^7.9.0", "d3-array": "^3.2.4", @@ -34,14 +34,14 @@ "d3-scale": "^4.0.2", "d3-scale-chromatic": "^3.1.0", "d3-shape": "^3.2.0", - "highlight.js": "^11.9.0", + "highlight.js": "^11.10.0", "svelte": "4.2.18", "svelte-multiselect": "^10.3.0", "svelte-zoo": "^0.4.10", - "three": "^0.165.0" + "three": "^0.166.1" }, "devDependencies": { - "@playwright/test": "^1.45.0", + "@playwright/test": "^1.45.2", "@sveltejs/adapter-static": "3.0.2", "@sveltejs/package": "^2.3.2", "@sveltejs/vite-plugin-svelte": "^3.1.1", @@ -52,29 +52,29 @@ "@types/d3-scale": "^4.0.8", "@types/d3-scale-chromatic": "^3.0.3", "@types/d3-shape": "^3.1.6", - "@types/three": "^0.165.0", - "@vitest/coverage-v8": "^1.6.0", - "eslint": "^9.5.0", - "eslint-plugin-svelte": "^2.41.0", + "@types/three": "^0.166.0", + "@vitest/coverage-v8": "^2.0.3", + "eslint": "^9.7.0", + "eslint-plugin-svelte": "^2.43.0", "hastscript": "^9.0.0", "jsdom": "^24.1.0", "mdsvex": "^0.11.2", "mdsvexamples": "^0.4.1", - "prettier": "^3.3.2", - "prettier-plugin-svelte": "^3.2.5", + "prettier": "^3.3.3", + "prettier-plugin-svelte": "^3.2.6", "rehype-autolink-headings": "^7.1.0", "rehype-katex-svelte": "^1.2.0", "rehype-slug": "^6.0.0", "remark-math": "3.0.0", "sharp": "^0.33.4", "svelte-check": "^3.8.4", - "svelte-preprocess": "^6.0.1", + "svelte-preprocess": "^6.0.2", "svelte-toc": "^0.5.9", "svelte2tsx": "^0.7.13", - "typescript": "5.5.2", - "typescript-eslint": "^7.14.1", - "vite": "^5.3.2", - "vitest": "^1.6.0" + "typescript": "5.5.3", + "typescript-eslint": "^7.16.1", + "vite": "^5.3.4", + "vitest": "^2.0.3" }, "keywords": [ "svelte", diff --git a/src/lib/labels.ts b/src/lib/labels.ts index c64c24e..5bf5f5b 100644 --- a/src/lib/labels.ts +++ b/src/lib/labels.ts @@ -57,6 +57,37 @@ export const pretty_num = (num: number, fmt?: string | number) => { } return format(fmt)(num) } +export function parse_si_float( + value: T, +): T | number | string { + // if not string, return as is + if (typeof value !== `string`) return value + // Remove whitespace and commas + const cleaned = value.trim().replace(/(\d),(\d)/g, `$1$2`) + + // Check if the value is a SI-formatted number (e.g., "1.23k", "4.56M", "789µ", "12n") + const match = cleaned.match(/^([-+]?\d*\.?\d+)\s*([yzafpnµmkMGTPEZY])?$/i) + if (match) { + const [, num_part, suffix] = match + let multiplier = 1 + if (suffix) { + const suffixes = `yzafpnµm kMGTPEZY` + const index = suffixes.indexOf(suffix) + if (index !== -1) { + multiplier = Math.pow(1000, index - 8) + } + } + return parseFloat(num_part) * multiplier + } + + // If it's a number without SI suffix, try parsing it + if (/^[-+]?[\d,]+\.?\d*$/.test(cleaned)) { + return parseFloat(cleaned) + } + + // If the value is not a formatted number, return as is + return value +} export const category_counts: Record = { actinide: 15, diff --git a/tests/unit/labels.test.ts b/tests/unit/labels.test.ts index 52d5bf5..cc2446b 100644 --- a/tests/unit/labels.test.ts +++ b/tests/unit/labels.test.ts @@ -2,11 +2,12 @@ import { element_data } from '$lib' import { default_fmt, heatmap_keys, + parse_si_float, pretty_num, property_labels, superscript_digits, } from '$lib/labels' -import { expect, test } from 'vitest' +import { describe, expect, test } from 'vitest' test(`pretty_num`, () => { expect(pretty_num(0)).toBe(`0`) @@ -61,3 +62,48 @@ test(`superscript_digits`, () => { expect(superscript_digits(`+123-456+789-0`)).toBe(`⁺¹²³⁻⁴⁵⁶⁺⁷⁸⁹⁻⁰`) expect(superscript_digits(`No digits here`)).toBe(`No digits here`) }) + +describe(`parse_si_float function`, () => { + test.each([ + [`123`, 123], // int + [`123.45`, 123.45], // float + [`1,234.45`, 1234.45], // with comma + [`1,234,567.89`, 1234567.89], // 2 commas + [`1k`, 1000], + [`1.5k`, 1500], + [`2M`, 2000000], + [`3.14G`, 3140000000], + [`5T`, 5000000000000], + [`1m`, 0.001], + [`500µ `, 0.0005], + [`10n`, 1e-8], + [`2p`, 2e-12], + [`3f`, 3e-15], + [`4a`, 4e-18], + [` 5z`, 5e-21], // leading whitespace + [`6y`, 6e-24], + [`-1.5k`, -1500], + [`-500µ`, -0.0005], + [`abc`, `abc`], + [``, ``], + [` 123 `, 123], // leading/trailing whitespace + [`-123`, -123], + [`1 k`, 1000], // with space + [`2 µ`, 0.000002], // with space + [`foo`, `foo`], + [`123foo`, `123foo`], + [-12, -12], // int -> int + [124.847321, 124.847321], // float -> float + [``, ``], // empty string + [undefined, undefined], // undefined + [null, null], // null + [`123.456.789`, `123.456.789`], // phone number + ])(`parseValue(%s) should return %s`, (input, expected) => { + const result = parse_si_float(input as string) + if (typeof expected === `number`) { + expect(result).toBeCloseTo(expected, 15) // Increased precision for very small numbers + } else { + expect(result).toEqual(expected) + } + }) +})