ThomasR/JSON.sortify

View on GitHub
src/index.js

Summary

Maintainability
A
0 mins
Test Coverage
/*!
*    Copyright 2015-2017 Thomas Rosenau
*
*    Licensed under the Apache License, Version 2.0 (the "License");
*    you may not use this file except in compliance with the License.
*    You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS,
*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*    See the License for the specific language governing permissions and
*    limitations under the License.
*/
'use strict';

/**
* Create a “sorted” version of an object.
*
* JS engines internally keep track of an object's keys in the order of
* creation time, i.e. {a:1,b:2} is treated differently from {b:2,a:1}.
* That difference can be seen when JSON.stringify is called on that object.
* This function normalizes any kind of object by rearranging the keys in
* alphabetical order (numerical keys first, since v8 does so, and there's
* nothing we can do about it).
* @param {*} o The object to be sorted
*/
const sortKeys = o => {
    if (Array.isArray(o)) {
        return o.map(sortKeys);
    }
    if (o instanceof Object) {
        // put numeric keys first
        let numeric = [];
        let nonNumeric = [];
        Object.keys(o).forEach(key => {
            if (/^(0|[1-9][0-9]*)$/.test(key)) {
                numeric.push(+key);
            } else {
                nonNumeric.push(key);
            }
        });
        // do the rearrangement
        return numeric.sort((a, b) => a - b).concat(nonNumeric.sort()).reduce((result, key) => {
            result[key] = sortKeys(o[key]); // recurse!
            return result;
        }, {});
    }
    return o;
};

const jsonStringify = JSON.stringify.bind(JSON); // this allows redefinition like JSON.stringify = require('json.sortify')

const sortify = (value, replacer, space) => {
    // replacer, toJSON(), cyclic references and other stuff is better handled by native stringifier.
    // So we do JSON.stringify(sortKeys( JSON.parse(JSON.stringify()) )).
    // This approach is slightly slower but much safer than a manual stringification.
    let nativeJson = jsonStringify(value, replacer, 0);
    if (!nativeJson || nativeJson[0] !== '{' && nativeJson[0] !== '[') { // if value is not an Object or Array
        return nativeJson;
    }
    let cleanObj = JSON.parse(nativeJson);
    return jsonStringify(sortKeys(cleanObj), null, space);
};

module.exports = sortify;