Mini Project: The Data Cleaner

Grade 13, page 11 of 12. A two-minute read.

In the real world, data is messy. Some fields are missing, some are empty, some have duplicates. Let's use our power-ups to clean up a messy list of sign-ups (the kind of thing that might come from an online form).

cleaner.js
const signups = [
  { name: "Mia", email: "mia@example.com", age: 21, tags: ["music", "coding"] },
  { name: "Omar", email: "omar@example.com", age: 0, tags: ["sports", "coding"] },
  { name: "", email: "mystery@example.com", tags: ["coding", "art"] },
  { name: "Zoe", email: "mia@example.com", age: 34, profile: { city: "Lisbon" } },
  { name: "Sam", email: "sam@example.com", age: 19, tags: ["music"], profile: { city: "Paris" } },
];

// 1. Remove duplicate emails (keep the first one we see)
const seenEmails = new Set();
const unique = signups.filter(({ email }) => {
  if (seenEmails.has(email)) return false;
  seenEmails.add(email);
  return true;
});

// 2. Fill in missing values with sensible defaults
const cleaned = unique.map(({ name, email, age, tags, profile }) => ({
  name: name || "Anonymous",          // empty name → "Anonymous"
  email,
  age: age ?? "unknown",              // keep 0, replace missing
  tags: tags ?? [],
  city: profile?.city ?? "not given",
}));

console.log(`${signups.length} sign-ups → ${cleaned.length} after removing duplicates\n`);
for (const { name, age, city, tags } of cleaned) {
  console.log(`${name.padEnd(10)} age: ${String(age).padEnd(8)} city: ${city.padEnd(10)} tags: ${tags.length}`);
}

// 3. Count interests across everyone, with a Map
const interestCounts = new Map();
for (const person of cleaned) {
  for (const tag of person.tags) {
    interestCounts.set(tag, (interestCounts.get(tag) ?? 0) + 1);
  }
}

const allTags = [...interestCounts.keys()];
console.log(`\nAll interests: ${allTags.join(", ")}`);
for (const [tag, count] of interestCounts) {
  console.log(`  ${tag}: ${"★".repeat(count)}`);
}

It prints

5 sign-ups → 4 after removing duplicates

Mia        age: 21       city: not given  tags: 2
Omar       age: 0        city: not given  tags: 2
Anonymous  age: unknown  city: not given  tags: 2
Sam        age: 19       city: Paris      tags: 1

All interests: music, coding, sports, art
  music: ★★
  coding: ★★★
  sports: ★
  art: ★

Look at all the power-ups at work:

  • A Set tracks which emails we've already seen, so the duplicate mia@example.com (Zoe's entry) is removed.
  • Destructuring in parameters pulls out each field neatly.
  • || replaces the empty name with "Anonymous", because we want any falsy name replaced.
  • ?? replaces only missing ages, so Omar's age of 0 is kept. (Using || here would have wrongly made Omar's age "unknown".)
  • ?. safely reaches into profile, which most people don't have.
  • A Map counts the interests, and spread turns its keys into an array.

This is exactly the kind of code professional developers write every day when handling real-world data.

Try it yourself

Add a few more messy sign-ups of your own (missing emails, weird ages, duplicate tags inside one person's list) and see if the cleaner handles them. Can you make it also remove duplicate tags within a single person's tags array? (Hint: [...new Set(tags)].)

🎓 CONGRATULATIONS! YOU'VE GRADUATED FROM HIGH SCHOOL! 🎓

Throw your cap in the air! You now know modern JavaScript: the fundamentals, the power tools, the DOM, events, and all the slick modern syntax. You can build real, interactive web pages.

And as promised, you've earned a full scholarship to our College. No essays, no applications. In College, you'll tackle the advanced topics that separate hobbyists from professionals: asynchronous code, classes, modules and tools, and finally, your capstone projects.

Reading is step one

CurlyScript turns these notes into a game: short questions, an explanation after every answer, and a test at the end of each grade. Free, with nothing to sign up for.

Start learning