How to sort a JSON object with sub-objects based on an attribute of its sub-objects?

Viewed 29

I have a list of objects that I need to index into by one of its attributes, the name in the below example. So I'm storing the list as an object instead of an array:

{
    "Foo": {
        "name": "Foo",
        "date": "2022-08-21"
    },
    "Bar": {
        "name": "Bar",
        "date": "2022-08-20"
    }
}

How can I sort the sub-objects by a different property of theirs (i.e. date in the above example), when serializing this JSON?

1 Answers

By definition, the keys within a JSON object are semantically unordered, so implementations are not required to support explicit sorting of such keys in any way.

However, as of version 1.4, jq does preserve the order of keys within objects, and also respects the order in which they are added. So, for the problem at hand, it becomes a matter of converting the above representation into an array, sorting it, and converting it back to a JSON object:

jq 'to_entries | sort_by(.value.date) | from_entries'

Explanation

First, convert the object with sub-objects into an array of key/value pairs with to_entries. The input becomes:

[
    {
        "key": "Foo",
        "value": {
            "obj1.name": "Foo",
            "obj1.date": "2022-08-21"
        }
    },
    {
        "key": "Bar",
        "value": {
            "obj2.name": "Bar",
            "obj2.date": "2022-08-20"
        }
    }
]

Since we now have an array, we can sort it with an arbitrary sub-object selector in sort_by, in my example, by .value.date, the output becomes:

[
    {
        "key": "Bar",
        "value": {
            "obj2.name": "Bar",
            "obj2.date": "2022-08-20"
        }
    },
    {
        "key": "Foo",
        "value": {
            "obj1.name": "Foo",
            "obj1.date": "2022-08-21"
        }
    }
]

Now it's a matter of converting the key/value form back to the object form with from_entries. The output becomes:

{
    "Bar": {
        "name": "Bar",
        "date": "2022-08-20"
    },
    "Foo": {
        "name": "Foo",
        "date": "2022-08-21"
    }
}
Related