Skip to content

Instantly share code, notes, and snippets.

@davidfurlong
Last active March 6, 2025 02:17
Show Gist options
  • Save davidfurlong/463a83a33b70a3b6618e97ec9679e490 to your computer and use it in GitHub Desktop.
Save davidfurlong/463a83a33b70a3b6618e97ec9679e490 to your computer and use it in GitHub Desktop.
JSON.stringify replacer function for having object keys sorted in output (supports deeply nested objects)
// Spec http://www.ecma-international.org/ecma-262/6.0/#sec-json.stringify
const replacer = (key, value) =>
value instanceof Object && !(value instanceof Array) ?
Object.keys(value)
.sort()
.reduce((sorted, key) => {
sorted[key] = value[key];
return sorted
}, {}) :
value;
// Usage
// JSON.stringify({c: 1, a: { d: 0, c: 1, e: {a: 0, 1: 4}}}, replacer);
@peternann
Copy link

This is quite clever. Touche.

@congzhou09
Copy link

Ingenious!

@mepanko91
Copy link

Excellent!
Now my getUniqueObjects function works correctly.

function getUniqueObjects(arr) {
  const replacer = (key, value) =>
    value && typeof value === "object" && !Array.isArray(value)
      ? Object.keys(value)
          .sort()
          .reduce((sorted, key) => {
            sorted[key] = value[key];
            return sorted;
          }, {})
      : value;
  return arr.filter(
    (v, i, a) =>
      a.findIndex(
        (v2) => JSON.stringify(v2, replacer) === JSON.stringify(v, replacer)
      ) === i
  );
}

@bcowgill
Copy link

bcowgill commented Feb 22, 2025

It fails to handle the fundamental Object types, converting number, boolean, etc into object...

JSON.stringify({c: new Number(1), a: { d: 0, c: 1, e: {a: 0, 1: 4}}}, replacer,2);
'{\n' +
' "a": {\n' +
' "c": 1,\n' +
' "d": 0,\n' +
' "e": {\n' +
' "1": 4,\n' +
' "a": 0\n' +
' }\n' +
' },\n' +
' "c": {}\n' + <<<<===== OOPS!
'}'

const replacer = (key, value) =>
value instanceof Object && !(value instanceof Array) && !(value instanceof Function) && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Boolean) && !(value instanceof Number) && !(value instanceof String) ?
Object.keys(value)
.sort()
.reduce((sorted, key) => {
sorted[key] = value[key];
return sorted
}, {}) :
value;

@congzhou09
Copy link

It fails to handle the fundamental Object types, converting number, boolean, etc into object...

JSON.stringify({c: new Number(1), a: { d: 0, c: 1, e: {a: 0, 1: 4}}}, replacer,2); '{\n' + ' "a": {\n' + ' "c": 1,\n' + ' "d": 0,\n' + ' "e": {\n' + ' "1": 4,\n' + ' "a": 0\n' + ' }\n' + ' },\n' + ' "c": {}\n' + <<<<===== OOPS! '}'

const replacer = (key, value) => value instanceof Object && !(value instanceof Array) && !(value instanceof Function) && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Boolean) && !(value instanceof Number) && !(value instanceof String) ? Object.keys(value) .sort() .reduce((sorted, key) => { sorted[key] = value[key]; return sorted }, {}) : value;

@bcowgill A nice edge case! What about using this expression
value instanceof Object && !Array.isArray(value) && Object.keys(value).length > 0
to make it more succinct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment