GitHunt
KO

kolodny/json-ordered

json-ordered

JSON stringify with custom key ordering based on a reference object.

Unlike json-stringify-stable which uses alphabetical ordering, json-ordered preserves the key order from a reference object. This is useful for maintaining consistent formatting across configuration files, API responses, or any scenario where you want new objects to match an existing key order.

Installation

npm install json-ordered

Usage

ES Modules

import { stringify, stringifier } from 'json-ordered';

CommonJS

const { stringify, stringifier } = require('json-ordered');

Basic Example

import { stringify } from 'json-ordered';

const reference = { z: 1, a: 2, m: 3 };
const target = { a: 10, z: 20, m: 30, b: 40 };

stringify(target, reference);
// '{"z":20,"a":10,"m":30,"b":40}'
//
// Keys from reference (z, a, m) come first in reference order,
// then new keys (b) are appended alphabetically

Reusable Stringifier

When stringifying multiple objects with the same key order, create a reusable function:

import { stringifier } from 'json-ordered';

const stringify = stringifier({ name: '', age: 0, city: '' });

stringify({ city: 'NYC', name: 'Alice', age: 30 });
// '{"name":"Alice","age":30,"city":"NYC"}'

stringify({ age: 25, city: 'LA', name: 'Bob', country: 'USA' });
// '{"name":"Bob","age":25,"city":"LA","country":"USA"}'

Nested Objects

Key ordering is preserved at every nesting level:

import { stringify } from 'json-ordered';

const reference = {
  z: 1,
  config: { port: 8080, host: 'localhost', timeout: 5000 },
  a: 2,
};

const target = {
  a: 100,
  config: { timeout: 3000, host: '0.0.0.0', port: 9000, ssl: true },
  z: 200,
};

stringify(target, reference, 2);
// {
//   "z": 200,
//   "config": {
//     "port": 9000,
//     "host": "0.0.0.0",
//     "timeout": 3000,
//     "ssl": true
//   },
//   "a": 100
// }

Arrays of Objects

Objects within arrays use the key order from the first object in the reference array:

import { stringify } from 'json-ordered';

const reference = {
  items: [{ z: 1, a: 2 }],
};

const target = {
  items: [
    { a: 10, z: 20 },
    { a: 30, z: 40, extra: 50 },
  ],
};

stringify(target, reference);
// '{"items":[{"z":20,"a":10},{"z":40,"a":30,"extra":50}]}'

Pretty Printing

Pass a space argument (same as JSON.stringify):

stringify(target, reference, null, 2);     // 2-space indent
stringify(target, reference, null, '\t');  // tab indent

Using a Replacer

Both functions support the replacer parameter from JSON.stringify:

import { stringify, stringifier } from 'json-ordered';

// Replacer function - omit keys with value 0
stringify({ a: 1, b: 0, c: 3 }, { c: 0, b: 0, a: 0 }, (k, v) => v === 0 ? undefined : v);
// '{"c":3,"a":1}'

// Replacer array - only include specific keys
const s = stringifier({ z: 1, a: 2, m: 3 });
s({ a: 10, z: 20, m: 30 }, ['z', 'a']);
// '{"z":20,"a":10}'

API

stringify(obj, referenceObj, replacer?, space?)

Stringify an object using a reference object's key order.

Parameter Type Description
obj any The object to stringify
referenceObj any The object whose key order should be used
replacer function | array | null Optional replacer (same as JSON.stringify)
space string | number Optional indentation (same as JSON.stringify)

Returns: string - JSON string with keys ordered according to the reference.

stringifier(referenceObj)

Creates a reusable stringify function with a cached key order map.

Parameter Type Description
referenceObj any The object whose key order should be preserved

Returns: (obj, replacer?, space?) => string - A stringify function

How It Works

  1. Reference keys first: Keys that exist in both reference and target are ordered according to the reference
  2. New keys appended: Keys only in the target are appended alphabetically
  3. Recursive ordering: Nested objects use key order from the corresponding nested reference object
  4. Array handling: All objects in an array use the key order from the first object in the reference array

Edge Cases

Keys Missing from Target

Keys in the reference but not in the target are simply skipped:

stringify({ a: 1 }, { z: 0, a: 0, m: 0 });
// '{"a":1}'

Empty Reference

With an empty reference, keys are sorted alphabetically:

stringify({ z: 1, a: 2, m: 3 }, {});
// '{"a":2,"m":3,"z":1}'

Numeric Keys

JavaScript always iterates numeric keys in ascending order. This is a language limitation:

stringify({ '2': 'b', '1': 'a' }, { '2': '', '1': '' });
// '{"1":"a","2":"b"}' - numeric keys are always sorted

Special Values

  • null values are preserved
  • undefined values are omitted (standard JSON behavior)
  • Date objects are serialized as ISO strings
  • Functions are omitted (standard JSON behavior)

TypeScript

Full TypeScript support with type declarations included.

import { stringify, createOrderer } from 'json-ordered';

interface Config {
  port: number;
  host: string;
}

const ref: Config = { port: 8080, host: 'localhost' };
const target: Config = { host: '0.0.0.0', port: 9000 };

stringify(target, ref); // '{"port":9000,"host":"0.0.0.0"}'

License

MIT

kolodny/json-ordered | GitHunt