Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 369 additions & 0 deletions playground/hash_pandas_object.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>tsb — hashPandasObject Playground</title>
<style>
:root {
--bg: #0d1117;
--surface: #161b22;
--border: #30363d;
--text: #e6edf3;
--accent: #58a6ff;
--green: #3fb950;
--orange: #d29922;
--red: #f85149;
--font-mono: "Cascadia Code", "Fira Code", "JetBrains Mono", monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
padding: 2rem;
max-width: 900px;
margin: 0 auto;
}
a { color: var(--accent); }
h1 { color: var(--accent); margin-bottom: 0.5rem; }
h2 { margin-top: 0; margin-bottom: 0.5rem; font-size: 1.25rem; }
p { color: #8b949e; margin-bottom: 1rem; }
code {
font-family: var(--font-mono);
font-size: 0.875em;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.3rem;
padding: 0.1rem 0.4rem;
}
.back { margin-bottom: 2rem; display: inline-block; }

/* ── Loading overlay ──────────────────────────────── */
#playground-loading {
position: fixed;
inset: 0;
background: rgba(13, 17, 23, 0.92);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
gap: 1rem;
}
.spinner {
width: 40px; height: 40px;
border: 3px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#playground-status { color: #8b949e; font-size: 0.95rem; }

/* ── Section cards ────────────────────────────────── */
.section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.section p { margin-bottom: 0.75rem; }

/* ── Playground block ─────────────────────────────── */
.playground-block {
margin-top: 0.75rem;
}
.playground-header {
display: flex;
align-items: center;
justify-content: space-between;
background: #1c2128;
border: 1px solid var(--border);
border-bottom: none;
border-radius: 0.5rem 0.5rem 0 0;
padding: 0.4rem 0.75rem;
}
.playground-label {
font-size: 0.75rem;
color: #8b949e;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.playground-actions {
display: flex;
gap: 0.5rem;
}
.playground-actions button {
background: transparent;
color: var(--accent);
border: 1px solid var(--border);
border-radius: 0.35rem;
padding: 0.25rem 0.7rem;
font-size: 0.8rem;
cursor: pointer;
font-family: system-ui, sans-serif;
transition: background 0.15s, border-color 0.15s;
}
.playground-actions button:hover:not(:disabled) {
background: rgba(88, 166, 255, 0.1);
border-color: var(--accent);
}
.playground-actions button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.playground-run { font-weight: 600; }

/* ── Editor (contenteditable pre) ─────────────────── */
.playground-editor {
display: block;
width: 100%;
min-height: 80px;
background: #0d1117;
color: var(--text);
border: 1px solid var(--border);
border-top: none;
border-bottom: none;
padding: 1rem;
font-family: var(--font-mono);
font-size: 0.875rem;
line-height: 1.55;
outline: none;
tab-size: 2;
white-space: pre;
overflow-x: auto;
}
.playground-editor:focus {
border-color: var(--accent);
box-shadow: inset 0 0 0 1px var(--accent);
}

/* ── Output area ──────────────────────────────────── */
.playground-output {
background: #1c2333;
border: 1px solid var(--border);
border-radius: 0 0 0.5rem 0.5rem;
padding: 0.75rem 1rem;
font-family: var(--font-mono);
font-size: 0.85rem;
color: #8b949e;
white-space: pre-wrap;
min-height: 2rem;
word-break: break-word;
}
.playground-output.active {
color: var(--green);
border-color: var(--green);
}
.playground-output.error {
color: var(--red);
border-color: var(--red);
}
.playground-hint {
font-size: 0.75rem;
color: #484f58;
margin-top: 0.35rem;
text-align: right;
}

/* ── Footer ───────────────────────────────────────── */
footer {
text-align: center;
padding: 2rem 0;
color: #8b949e;
font-size: 0.85rem;
border-top: 1px solid var(--border);
margin-top: 2rem;
}
</style>
</head>
<body>

<!-- Loading overlay (hidden once TS compiler + tsb are ready) -->
<div id="playground-loading">
<div class="spinner"></div>
<div id="playground-status">Initializing playground…</div>
</div>

<a class="back" href="index.html">← Back to roadmap</a>
<h1>📦 hashPandasObject — Interactive Playground</h1>
<p>
<code>hashPandasObject(obj)</code> computes FNV-1a 64-bit hash values for each element
of a <code>Series</code> or each row of a <code>DataFrame</code> — mirroring
<code>pandas.util.hash_pandas_object</code>.<br>
<strong style="color: var(--text);">Edit any code block below and press ▶ Run
(or Ctrl+Enter) to execute it live in your browser.</strong>
</p>

<!-- ── 1. Series hashing ───────────────────────────── -->
<div class="section">
<h2>1 · Series hashing</h2>
<p>
Hash each element of a <code>Series</code>. Identical values produce identical hashes;
pass <code>{ index: false }</code> to ignore the index label when computing the hash.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" title="Run (Ctrl+Enter)" disabled>▶ Run</button>
<button class="playground-reset" title="Reset">↺ Reset</button>
</div>
</div>
<pre class="playground-editor" contenteditable="true" spellcheck="false">import { Series, hashPandasObject } from "tsb";

const s = new Series({ data: ["apple", "banana", "apple"], index: [0, 1, 2] });
const h = hashPandasObject(s, { index: false });

// Same value → same hash
console.log("apple===apple:", h.iat(0) === h.iat(2)); // true
console.log("apple===banana:", h.iat(0) === h.iat(1)); // false
console.log("hashes:", [...h.values]);</pre>
<pre class="playground-output">Click ▶ Run to execute</pre>
<div class="playground-hint">Ctrl+Enter to run</div>
</div>
</div>

<!-- ── 2. DataFrame row hashing ────────────────────── -->
<div class="section">
<h2>2 · DataFrame row hashing</h2>
<p>
Hash each row of a <code>DataFrame</code>. Rows with identical values across all columns
produce the same hash, making this useful for deduplication and change detection.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" title="Run (Ctrl+Enter)" disabled>▶ Run</button>
<button class="playground-reset" title="Reset">↺ Reset</button>
</div>
</div>
<pre class="playground-editor" contenteditable="true" spellcheck="false">import { DataFrame, hashPandasObject } from "tsb";

const df = new DataFrame({
id: [1, 2, 3],
name: ["Alice", "Bob", "Alice"],
age: [30, 25, 30],
});

const rowHashes = hashPandasObject(df, { index: false });
// Rows 0 and 2 are identical → same hash
console.log("row0===row2:", rowHashes.iat(0) === rowHashes.iat(2)); // true
console.log("row0===row1:", rowHashes.iat(0) === rowHashes.iat(1)); // false</pre>
<pre class="playground-output">Click ▶ Run to execute</pre>
<div class="playground-hint">Ctrl+Enter to run</div>
</div>
</div>

<!-- ── 3. Deduplication with hashes ────────────────── -->
<div class="section">
<h2>3 · Deduplication with hashes</h2>
<p>
Use row hashes to find unique rows efficiently — a common pattern when
<code>duplicated()</code> is too slow on large DataFrames.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" title="Run (Ctrl+Enter)" disabled>▶ Run</button>
<button class="playground-reset" title="Reset">↺ Reset</button>
</div>
</div>
<pre class="playground-editor" contenteditable="true" spellcheck="false">import { DataFrame, hashPandasObject } from "tsb";

const df = new DataFrame({
a: [1, 2, 1, 3],
b: ["x", "y", "x", "z"],
});

const hashes = hashPandasObject(df, { index: false });
const seen = new Set<number>();
const uniqueRows: number[] = [];

for (let i = 0; i < df.shape[0]; i++) {
const h = hashes.iat(i);
if (!seen.has(h)) {
seen.add(h);
uniqueRows.push(i);
}
}
// uniqueRows = [0, 1, 3] — row 2 is a duplicate of row 0
console.log("unique row indices:", uniqueRows);</pre>
<pre class="playground-output">Click ▶ Run to execute</pre>
<div class="playground-hint">Ctrl+Enter to run</div>
</div>
</div>

<!-- ── 4. Controlling index inclusion ──────────────── -->
<div class="section">
<h2>4 · Controlling index inclusion</h2>
<p>
By default (<code>index: true</code>), the index label is mixed into the hash.
Set <code>index: false</code> to hash only the values.
</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" title="Run (Ctrl+Enter)" disabled>▶ Run</button>
<button class="playground-reset" title="Reset">↺ Reset</button>
</div>
</div>
<pre class="playground-editor" contenteditable="true" spellcheck="false">import { Series, hashPandasObject } from "tsb";

const s = new Series({ data: [42, 42], index: ["a", "b"] });

// index=true (default): different index → different hash
const withIdx = hashPandasObject(s, { index: true });
console.log("index=true, iat(0)===iat(1):", withIdx.iat(0) === withIdx.iat(1)); // false

// index=false: only values matter
const noIdx = hashPandasObject(s, { index: false });
console.log("index=false, iat(0)===iat(1):", noIdx.iat(0) === noIdx.iat(1)); // true</pre>
<pre class="playground-output">Click ▶ Run to execute</pre>
<div class="playground-hint">Ctrl+Enter to run</div>
</div>
</div>

<!-- ── 5. Scratch Pad ──────────────────────────────── -->
<div class="section">
<h2>🧪 Scratch Pad</h2>
<p>Write your own <code>hashPandasObject</code> code below. All exports from <code>tsb</code> are available.</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript — Scratch Pad</span>
<div class="playground-actions">
<button class="playground-run" title="Run (Ctrl+Enter)" disabled>▶ Run</button>
<button class="playground-reset" title="Reset">↺ Reset</button>
</div>
</div>
<pre class="playground-editor" contenteditable="true" spellcheck="false">import { Series, DataFrame, hashPandasObject } from "tsb";

// Try it! Hash a Series of numbers.
const nums = new Series({ data: [10, 20, 10, 30] });
const hashes = hashPandasObject(nums, { index: false });

console.log("10===10:", hashes.iat(0) === hashes.iat(2));
console.log("10===20:", hashes.iat(0) === hashes.iat(1));
console.log("all hashes:", [...hashes.values]);</pre>
<pre class="playground-output">Click ▶ Run to execute</pre>
<div class="playground-hint">Ctrl+Enter to run</div>
</div>
</div>

<footer>
<p>
<a href="index.html">tsb playground</a> ·
Built by <a href="https://github.com/githubnext/autoloop">Autoloop</a>
</p>
</footer>

<!-- Playground runtime: loads TypeScript compiler + tsb bundle, sets up editors -->
<script type="module" src="playground-runtime.js"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,11 @@ <h3><a href="style.html" style="color: var(--accent); text-decoration: none;">
<p>dataFrameStyle(df) · highlightMax / highlightMin / highlightNull / highlightBetween · backgroundGradient / textGradient · barChart · format / formatIndex · apply / applymap / map · setCaption / setTableStyles / hide · toHtml / toLatex. Mirrors pandas.DataFrame.style (Styler).</p>
<div class="status done">✅ Complete</div>
</div>
<div class="feature-card">
<h3><a href="hash_pandas_object.html" style="color: var(--accent); text-decoration: none;">🔑 hashPandasObject — FNV-1a Hashing</a></h3>
<p>hashPandasObject(s) · hashPandasObject(df) · index option. Mirrors pandas.util.hash_pandas_object. FNV-1a 64-bit per element or row.</p>
<div class="status done">✅ Complete</div>
</div>
</section>
<div class="features-grid">
<div class="feature-card">
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,5 @@ export type {
GradientOptions,
BarOptions,
} from "./stats/index.ts";
export { hashPandasObject } from "./stats/index.ts";
export type { HashPandasObjectOptions } from "./stats/index.ts";
Loading
Loading