Minify and Extend
Pretty-printing makes JSON readable. Sometimes you want the opposite - the smallest possible string to send over the wire or stuff into a config field. This phase adds a minify mode, then a key-sorting option that makes output stable enough to diff, and finishes with directions to keep going after the weekend's over.
Minify is the same call, minus the indent
You already know how to minify. It's JSON.stringify with no indent argument. Parse to clean out whatever spacing was there, stringify with no spacing to get the tightest valid form.
Guess roughly how many characters shorter the minified version will be before you run it and see the real number.
const spaced = `{
"name": "Ada",
"langs": [ "Pascal", "Ada" ],
"active": true
}`;
const small = ;
console.log;
console.log;
Run it. The multi-line input collapses to one line and you'll see the character count drop. Parsing first matters - it guarantees the output is valid JSON, not a clumsy find-and-replace on whitespace that would mangle spaces inside string values.
One function, both directions
There's no reason to keep two functions. A mode argument picks pretty or compact, and both share the same parse.
const raw = '{"id":7,"tags":["x","y"]}';
console.log;
console.log;
console.log;
console.log;
Run it and watch the same input come out two ways. That's your formatter and your minifier in one place.
Sorting keys for stable output
Here's a problem you hit the moment you try to compare two JSON files: object key order. {"a":1,"b":2} and {"b":2,"a":1} are the same data, but as text they're different lines, and any diff tool will light up red. The fix is to sort keys before stringifying, so the same data always produces the same text.
That third argument to JSON.stringify - the replacer we skipped in Phase 1 - has a second form worth knowing about. We'll use it here, alongside the function form from a moment ago.
Your turn. sortedFormat is the point of this phase - the thing that makes two files holding the same data produce identical text. Have a go before you read on. Fill it in and hit Run: the checks underneath tell you whether it works. My version is in the next block whenever you want it.
// --- checks: fix your function until this prints "All good." ---
const messy = '{"name":"Ada","age":36,"admin":true,"name2":"x"}';
const out = ;
if
const keysInOrder = .;
const expectedOrder = ;
if
const parsed = JSON.;
if
const nested = '{"b":{"z":1,"a":2},"a":1}';
const nestedOut = ;
const nestedKeys = .;
if
console.log;
Stuck on reaching the nested keys too? The function-form replacer runs once for every key anywhere in the structure, not just the top-level ones - that's how you can collect all of them in a single pass.
One way to write it
const messy = '{"name":"Ada","age":36,"admin":true,"name2":"x"}';
console.log;
console.log;
console.log;
console.log;
That second form: pass the replacer an array of keys and it outputs only those keys, in that order - hand it the sorted list of every key and you get sorted output. We use the replacer-as-function form once to collect all keys, then the replacer-as-array form to emit them in sorted order. Run it. The first block keeps the original key order; the second alphabetizes every key. Now two files holding the same data, formatted this way, produce identical text - and a diff between them shows only real differences.
The whole tool, assembled
Here's everything from all four phases in one place - parse, pretty-print, explain errors, check shape, minify, sort. This is the thing you set out to build.
const raw = '{"name":"Ada","age":36,"admin":true}';
console.log; // pretty
console.log;
console.log;
console.log;
console.log;
console.log; // broken
Run it. One function, one options object, every behavior we built. Pretty by default, minify on request, sorted on request, shape-checked when you hand it a shape, and a clean error report when the text won't parse. That's a real tool.
Where to take it next
You've got a working formatter and validator. Here are directions worth a future afternoon - each builds on what's already here, none needs a library.
| Idea | The seed |
|---|---|
| Diff two JSONs | Parse both, walk keys, report added / removed / changed |
| Nested shape check | Let a shape value be another shape and recurse |
| Highlight | Wrap strings, numbers, keys in spans for colour in a page |
| Sort + diff combo | Sort both inputs first so the diff shows only real changes |
The diff is the most fun, so here's a starting sketch - a shallow compare of two flat objects that reports what changed.
Before you run it, count by eye: how many keys change value between before and after, and how many are brand new?
const before = ;
const after = ;
console.log;
Run it. You get age changed, admin changed, and email added - a small, clear diff. Notice it uses JSON.stringify to compare values, the same trick that lets it tell [1,2] apart from [1,3] without writing a deep-equality function. Make it recurse into nested objects and you've got something genuinely useful.
That's the build. You started with a one-line mess and ended with a tool that formats it, tells you when it's broken and where, checks it's the data you meant, shrinks it back down, and can even tell you what changed between two versions - every line of which you understand, because you ran all of it yourself.