DEV Community

Cover image for Building a Pick a Number Wheel: 4 Gotchas When Numbers Are Your Data
Devid E
Devid E

Posted on

Building a Pick a Number Wheel: 4 Gotchas When Numbers Are Your Data

I added a random number page to my NameWheelPro project, and numbers turned out to be trickier than names. The whole thing is vanilla JS with zero dependencies, so every fix below works in any project.

If you're building your own pick a number wheel, here are four gotchas worth knowing, with the code.

  1. sort() puts 10 before 2

The default sort is alphabetical, even for numbers:

['1', '10', '2', '20', '3'].sort();
// ["1", "10", "2", "20", "3"], not what anyone wants
Enter fullscreen mode Exit fullscreen mode

localeCompare has a numeric mode that fixes this and still handles text:

const sortEntries = (arr) =>
  [...arr].sort((a, b) =>
    a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
  );

sortEntries(['10', '2', 'Bob', '1']); // ["1", "2", "10", "Bob"]
Enter fullscreen mode Exit fullscreen mode
  1. Let people type ranges

If your tool accepts custom lists, nobody wants to type 1 through 50 one by one. Parsing 1-10, 15, 20-25 takes a few lines:

function parseCustom(text, max = 500) {
  const out = [];
  for (const part of text.split(/[\n,]+/)) {
    const s = part.trim();
    if (!s) continue;

    const m = s.match(/^(\d+)\s*-\s*(\d+)$/);
    if (m) {
      let [a, b] = [Number(m[1]), Number(m[2])];
      if (a > b) [a, b] = [b, a];                       // "10-5" still works
      for (let n = a; n <= b && out.length < max; n++) out.push(String(n));
    } else if (out.length < max) {
      out.push(s);
    }
  }
  return out;
}
Enter fullscreen mode Exit fullscreen mode

The max cap matters. Without it, someone typing 1-99999999 freezes the tab.

  1. Prove the randomness

crypto.getRandomValues is a good source, but random % 100 isn't perfectly uniform because 2³² doesn't divide evenly by 100. The bias is tiny, but rejection sampling removes it:

function randomInt(max) {
  const limit = Math.floor(2 ** 32 / max) * max;   // largest unbiased multiple
  const buf = new Uint32Array(1);
  do { crypto.getRandomValues(buf); } while (buf[0] >= limit);
  return buf[0] % max;
}
Enter fullscreen mode Exit fullscreen mode

Then test it instead of trusting it:

const counts = new Array(100).fill(0);
for (let i = 0; i < 1_000_000; i++) counts[randomInt(100)]++;
console.log(Math.min(...counts), Math.max(...counts));
// both should land roughly between 9,700 and 10,300
Enter fullscreen mode Exit fullscreen mode

If one number wins far more often, you have a bug. It's a 10-second test that builds real confidence.

  1. Tell users when you drop input

Capping entries protects performance, but silently discarding the rest looks like a bug. Return how many were dropped so the UI can say so:

const MAX_ENTRIES = 500;

function addEntries(current, incoming) {
  const room = Math.max(MAX_ENTRIES - current.length, 0);
  const accepted = incoming.slice(0, room);
  return {
    entries: [...current, ...accepted],
    dropped: incoming.length - accepted.length,   // "12 entries were not added"
  };
}
Enter fullscreen mode Exit fullscreen mode

Wrapping up

None of these is hard, but together they make a number tool feel trustworthy: correct sorting, forgiving input, verified fairness, and honest feedback.

You can try the live pick a number wheel here, with no signup. What's the sneakiest "it works on my machine" bug you've hit with numbers?

Top comments (0)