Created
January 26, 2026 18:59
-
-
Save ggorlen/b3f6002d5ce25b5f7f7afeffbcbcc4b1 to your computer and use it in GitHub Desktop.
Number analyzer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="description" content="Number Analyzer" /> | |
| <meta name="color-scheme" content="dark light" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Number Analyzer</title> | |
| <style> | |
| body { | |
| font-family: monospace; | |
| padding: 2em; | |
| max-width: 600px; | |
| background-color: Canvas; | |
| color: CanvasText; | |
| } | |
| textarea { | |
| width: 100%; | |
| height: 200px; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| padding: 10px; | |
| box-sizing: border-box; | |
| resize: vertical; | |
| background-color: Canvas; | |
| color: CanvasText; | |
| border: 1px solid; | |
| border-radius: 6px; | |
| } | |
| table { | |
| width: 100%; | |
| margin-top: 1em; | |
| border-collapse: collapse; | |
| table-layout: fixed; | |
| } | |
| th, | |
| td { | |
| padding: 6px 12px; | |
| border: 1px solid; | |
| text-align: left; | |
| width: 50%; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| #highlighted-text { | |
| white-space: pre-wrap; | |
| margin-top: 10px; | |
| padding: 10px; | |
| border: 1px dashed; | |
| border-radius: 6px; | |
| display: none; | |
| } | |
| .highlighted { | |
| background-color: #3e8d5e; | |
| font-weight: bold; | |
| border-radius: 2px; | |
| color: CanvasText; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Number Analyzer</h1> | |
| <textarea | |
| id="input" | |
| placeholder="Type numbers separated by anything..." | |
| ></textarea> | |
| <table id="stats-table"> | |
| <tr> | |
| <th>Sum</th> | |
| <td id="sum">0</td> | |
| </tr> | |
| <tr> | |
| <th>Count</th> | |
| <td id="count">0</td> | |
| </tr> | |
| <tr> | |
| <th>Average</th> | |
| <td id="average">0</td> | |
| </tr> | |
| <tr> | |
| <th>Median</th> | |
| <td id="median">0</td> | |
| </tr> | |
| <tr> | |
| <th>Min</th> | |
| <td id="min">0</td> | |
| </tr> | |
| <tr> | |
| <th>Max</th> | |
| <td id="max">0</td> | |
| </tr> | |
| </table> | |
| <div id="highlighted-text"></div> | |
| <script> | |
| // TODO: fix bug where '2.2.2.2 5' correctly ignores 2.2.2.2 but still highlights it | |
| const textarea = document.getElementById("input"); | |
| const sumEl = document.getElementById("sum"); | |
| const countEl = document.getElementById("count"); | |
| const avgEl = document.getElementById("average"); | |
| const medianEl = document.getElementById("median"); | |
| const minEl = document.getElementById("min"); | |
| const maxEl = document.getElementById("max"); | |
| const highlightEl = document.getElementById("highlighted-text"); | |
| // Median helper | |
| function median(arr) { | |
| const sorted = [...arr].sort((a, b) => a - b); | |
| if (sorted.length === 0) return 0; | |
| const mid = Math.floor(sorted.length / 2); | |
| return sorted.length % 2 === 0 | |
| ? (sorted[mid - 1] + sorted[mid]) / 2 | |
| : sorted[mid]; | |
| } | |
| // Parse numbers with strict rules | |
| function parseNumbers(text) { | |
| text = text.trim(); | |
| if (text === "") return []; | |
| // Special delimiter case: entire input is digits+periods, more than 1 period | |
| if (/^[\d.]+$/.test(text) && (text.match(/\./g) || []).length > 1) { | |
| return text | |
| .split(".") | |
| .filter((s) => s !== "") | |
| .map(Number); | |
| } | |
| // Otherwise, split by non-digit/non-dot/non-minus | |
| const candidates = text.split(/[^0-9.\-]+/); | |
| const numbers = []; | |
| for (let c of candidates) { | |
| if (c === "") continue; | |
| // Trim single trailing dot (e.g., 2.2. -> 2.2) | |
| if (c.endsWith(".") && (c.match(/\./g) || []).length === 2) { | |
| c = c.slice(0, -1); | |
| } | |
| const dotCount = (c.match(/\./g) || []).length; | |
| // Only accept numbers with 0 or 1 dot | |
| if (dotCount <= 1 && !isNaN(Number(c))) { | |
| numbers.push(Number(c)); | |
| } | |
| // Multi-dot numbers ignored completely | |
| } | |
| return numbers; | |
| } | |
| // Highlight valid numbers only | |
| function highlightValidNumbers(text) { | |
| return text.replace(/-?\d*\.?\d+\.?/g, (m) => { | |
| let candidate = m; | |
| if ( | |
| candidate.endsWith(".") && | |
| (candidate.match(/\./g) || []).length === 2 | |
| ) { | |
| candidate = candidate.slice(0, -1); | |
| } | |
| const dotCount = (candidate.match(/\./g) || []).length; | |
| if (dotCount <= 1 && !isNaN(Number(candidate))) { | |
| return `<span class="highlighted">${candidate}</span>`; | |
| } | |
| return m; // ignore invalid numbers entirely | |
| }); | |
| } | |
| function updateStats() { | |
| const text = textarea.value; | |
| const numbers = parseNumbers(text); | |
| if (numbers.length === 0) { | |
| sumEl.textContent = 0; | |
| countEl.textContent = 0; | |
| avgEl.textContent = 0; | |
| medianEl.textContent = 0; | |
| minEl.textContent = 0; | |
| maxEl.textContent = 0; | |
| highlightEl.style.display = "none"; | |
| highlightEl.innerHTML = ""; | |
| return; | |
| } | |
| const sum = numbers.reduce((a, b) => a + b, 0); | |
| const count = numbers.length; | |
| const avg = sum / count; | |
| const min = Math.min(...numbers); | |
| const max = Math.max(...numbers); | |
| const med = median(numbers); | |
| sumEl.textContent = sum; | |
| countEl.textContent = count; | |
| avgEl.textContent = avg; | |
| medianEl.textContent = med; | |
| minEl.textContent = min; | |
| maxEl.textContent = max; | |
| const highlighted = highlightValidNumbers(text); | |
| highlightEl.innerHTML = highlighted; | |
| highlightEl.style.display = "block"; | |
| } | |
| textarea.addEventListener("input", updateStats); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment