4

Sort the objects by keys whose value is also an object and sort that internal object as well i.e sort the object recursively. Sorting should be as per key.

I looked into Stackoverflow's other questions but None of them is for Object Recursive Sorting.

Question I looked into:

Sorting JavaScript Object by property value

Example:

input = {
    "Memo": {
        "itemAmount1": "5",
        "taxName1": "TAX",
        "productPrice1": "10",
        "accountName1": "Account Receivable (Debtors)"
    },
    "Footer": {
        "productDescription2": "Maggie",
        "itemQuantity2": "49.5",
        "accountName2": "Account Receivable (Debtors)",
        "taxName2": "TAX"
    },
    "Header": {
        "itemDiscount3": "10",
        "accountName3": "Account Receivable (Debtors)",
        "productPrice3": "10",
        "taxName3": "TAX"
    }
}

Output

output = {
    "Footer": {
        "accountName2": "Account Receivable (Debtors)",
        "itemQuantity2": "49.5",
        "productDescription2": "Maggie",
        "taxName2": "TAX"
    },
    "Header": {
        "accountName3": "Account Receivable (Debtors)",
        "itemDiscount3": "10",
        "productPrice3": "10",
        "taxName3": "TAX"
    },
    "Memo": {
        "accountName1": "Account Receivable (Debtors)",
        "itemAmount1": "5",
        "productPrice1": "10",
        "taxName1": "TAX"
    }
}

It is not necessary that it is 2 level object hierarchy it may contain n level of object hierarchy which need to be sorted.

Community
  • 1
  • 1
ksr89
  • 229
  • 3
  • 9
  • 6
    Javascript objects have no order, they are a key-value map. You *can't* order them. – deceze Jul 08 '14 at 10:52
  • Fixed the title: it said "property" instead of key (which cannot be used to sort, as @deceze points) – Pablo Lozano Jul 08 '14 at 10:55
  • Why can't we sort the keys in alphabetic order? – Gaurav Ramanan Jul 08 '14 at 10:57
  • 1
    As pointed out in this answer: http://stackoverflow.com/a/5525812/2964675 it's not in the ECMAScript spec. If you want some sort of order, try Array Objects. – MarcoL Jul 08 '14 at 11:01
  • @MarcoCI @_deceze this link is pointing problem of Browsers, I checked that when I create an object in chrome's console, it gives me an already sorted object but I want to sort an object using **node.js**. – ksr89 Jul 08 '14 at 12:32
  • which comes first: your height, your age, the colour of your eyes, or your weight? *properties* of an object have no _order_, nor should they. Use another ordered structure (such as array) in tandem with the object. – Mulan Jul 08 '20 at 15:11

5 Answers5

6

I think what @ksr89 means is that when we apply a for - in loop, we get keys in sorted order. I think this is a valid use case especially in the development of Node.js based ORMs

The following function should work and is I think what you are looking for.

 input = {
    "Memo": {
        "itemAmount1": "5",
        "taxName1": "TAX",
        "productPrice1": "10",
        "accountName1": "Account Receivable (Debtors)"
    },
    "Footer": {
        "productDescription2": "Maggie",
        "itemQuantity2": "49.5",
        "accountName2": "Account Receivable (Debtors)",
        "taxName2": "TAX"
    },
    "Header": {
        "itemDiscount3": "10",
        "accountName3": "Account Receivable (Debtors)",
        "productPrice3": "10",
        "taxName3": "TAX"
    }
}
window.sortedObject = sort(input);

function sort(object){
    if (typeof object != "object" || object instanceof Array) // Not to sort the array
        return object;
    var keys = Object.keys(object);
    keys.sort();
    var newObject = {};
    for (var i = 0; i < keys.length; i++){
        newObject[keys[i]] = sort(object[keys[i]])
    }
    return newObject;
}
for (var key in sortedObject){
    console.log (key);
    //Prints keys in order
}
ksr89
  • 229
  • 3
  • 9
Gaurav Ramanan
  • 3,405
  • 2
  • 19
  • 29
  • Nothing guarantees the order of the keys when looping over them using `for..in`. It will *probably* keep the order that the object was created with, but again, there's no guarantee for that and may work completely differently in different Javascript implementations. – deceze Jul 08 '14 at 11:34
  • @deceze I have tested this with Chrome and therefore it will work in Node.js. I will try in other browsers & post here. I am aware this is not part of the ECMA standard but it is a good enough solution for Node.js applications, specifically here where ksr89 is trying to implement an ORM I think. – Gaurav Ramanan Jul 09 '14 at 05:30
  • It may happen that `object` where its keys needs ordering is inside of a list/array. This function won't sort it. – NeverEndingQueue Oct 22 '19 at 08:33
2

I was on this page to write the following information. The code is based on Gaurav Ramanan's answer, but handles arrays and null differently.

Comparing JSON

To compare data from JSON files you may want to have them formatted the same way

  • from javascript: JSON.stringify(JSON.parse(jsonString), null, '\t') the last parameter could also be a number of spaces the last 2 parameters are optional (minified output if absent)
  • from Visual Studio Code: with the Prettify JSON extension

Verify indentation (i.e. TABs) and line endings (i.e. Unix). Also, keys may be recursively sorted during formatting.

Sorting keys with javascript:

const {isArray} = Array
const {keys} = Object

function sortKeysRec(obj) {
    if (isArray(obj)) {
        const newArray = []
        for (let i = 0, l = obj.length; i < l; i++)
            newArray[i] = sortKeysRec(obj[i])
        return newArray
    }
    if (typeof obj !== 'object' || obj === null)
        return obj
    const sortedKeys = keys(obj).sort()
    const newObject = {}
    for (let i = 0, l = sortedKeys.length; i < l; i++)
        newObject[sortedKeys[i]] = sortKeysRec(obj[sortedKeys[i]])
    return newObject
}

Ensure unix line endings with javascript: jsonString.replace(/\r\n/ug, '\n').

Rivenfall
  • 1,055
  • 10
  • 15
1

The solution above works only for the current implementation detail of node.js.

The ECMAScript standard doesn't guarantee any order for the keys iteration.

That said, the only solution I can think of is to use an array as support to sort the properties of the object and iterate on it:

var keys = Object.keys(object);
keys.sort();

for (var i = 0; i < keys.length; i++){
// this won't break if someone change NodeJS or Chrome implementation
    console.log(keys[i]);
}
LStarky
  • 2,685
  • 1
  • 16
  • 47
MarcoL
  • 9,599
  • 3
  • 35
  • 50
1

As this has recently been revived, I think it's worth pointing out again that we should generally treat objects as unordered collections of properties. Although ES6 did specify key traversal order (mostly first-added to last-added properties, but with a twist for integer-like keys), depending on this feels as though you're misusing your type. If it's ordered, use an array.

That said, if you are determined to do this, then it's relatively simple with ES6:

const sortKeys = (o) =>
  Object (o) !== o || Array .isArray (o)
    ? o
    : Object .keys (o) .sort () .reduce ((a, k) => ({...a, [k]: sortKeys (o [k])}), {})

const input = {Memo: {itemAmount1: "5", taxName1: "TAX", productPrice1: "10", accountName1: "Account Receivable (Debtors)"}, Footer: {productDescription2: "Maggie", itemQuantity2: "49.5", accountName2: "Account Receivable (Debtors)", taxName2: "TAX"}, Header: {itemDiscount3: "10", accountName3: "Account Receivable (Debtors)", productPrice3: "10", taxName3: "TAX"}}

console .log (
  sortKeys(input)
)
.as-console-wrapper {min-height: 100% !important; top: 0}

Note that there is a potential performance issue here as described well by Rich Snapp. I would only spend time fixing it if it turned out to be a bottleneck in my application, but if we needed to we could fix that issue with a version more like this:

const sortKeys = (o) =>
  Object (o) !== o || Array .isArray (o)
    ? o
    : Object .keys (o) .sort () .reduce ((a, k) => ((a [k] = sortKeys (o [k]), a)), {})

While this works, the addition of the comma operator and the use of property assignment make it uglier to my mind. But either one should work.

Scott Sauyet
  • 44,568
  • 4
  • 43
  • 95
0

Following up on @Gaurav Ramanan 's answer here's a shorter ES6 approach:

function sort(obj) {
  if (typeof obj !== "object" || Array.isArray(obj))
    return obj;
  const sortedObject = {};
  const keys = Object.keys(obj).sort();
  keys.forEach(key => sortedObject[key] = sort(obj[key]));
  return sortedObject;
}

The first condition will simply ensure that you only parse a valid object. So if it's not, it will return immediately with the original value unchanged.

Then an empty object is assigned because it will be used in the forEach loop where it will be mutated with the final sorted result.

The output will be a recursively sorted object.