Reputation: 2304
I was wondering if there is a way to re-organize the fields of an object using jq.
I mean, given that
{
"prop1": 1,
"prop2": {
"nested": 0
},
"prop3": true
}
I'd like to get this
{
"prop1": 1,
"prop3": true,
"prop2": {
"nested": 0
}
}
I want to choose the order of the fields (without any consideration about the type of the field or the alphabetical order, my choice only :) )
Thanks !
Upvotes: 3
Views: 1390
Reputation: 116919
In a followup question, @ManuelFahndrich asked
Do you see a way to include properties not named in the keys by default?
Here's a simple approach using to_entries
and from_entries
to perform "object subtraction":
{prop1, prop3, prop2} as $first
| $first + (to_entries - ($first|to_entries) | from_entries)
Upvotes: 0
Reputation: 116919
[The following is an update of the answer originally posted in August 2015 (!).]
For jq versions 1.4 and up, the simplest way to specify a particular order of keys explicitly is to use the
{foo, bar, ...}
form for specifying objects. E.g. if input.json has the JSON shown in the question, then:
jq -Mc '{prop1, prop3, prop2}' input.json
produces:
{"prop1":1,"prop3":true,"prop2":{"nested":0}}
In this case, only the ordering of the top-level keys is affected.
The OP indicated a need to normalize JSON so that a text-oriented diff
can be used. For such purposes, the normalization of objects no matter how deeply nested would typically be required. For this, jq (versions 1.4 and up) has the -S command-line option.
[The original answer with updates follows.]
The following is a variation of "inorder" given above. The desired JSON object (not a stringified version) is constructed directly. This works with jq versions 1.4 and up, since (by default) the keys are effectively stored in order of creation.
def orderKeys(keys):
. as $in
| reduce keys[] as $key ({}; . + { ($key): $in[$key] });
# Example:
orderKeys( ["prop1", "prop3", "prop2"] )
With the above in a file named order-keys.jq, and using the JSON in the task description as input, the command:
$ jq -M -n -c -f order-keys.jq
produces:
{"prop1":1,"prop3":true,"prop2":{"nested":0}}
Upvotes: 3
Reputation: 665
Ok, answering my own followup question. Here's my hacky solution to include the "rest" of the properties (not specified in the order) at the end of the resulting object, so as to not lose any of the input:
def orderKeys(orderedkeys):
. as $in
| reduce keys[] as $key ({}; if orderedkeys|contains([$key]) then . else . + { ($key): $in[$key] } end)
| . as $rest
| reduce orderedkeys[] as $key ({}; . + { ($key): $in[$key] })
| . + $rest;
# Example:
orderKeys( ["prop1", "prop3", "prop2"] )
On the input
{
"prop1": 1,
"otherprop": "foo",
"prop2": {
"nested": 0
},
"prop3": true
}
this produces
{
"prop1": 1,
"prop3": true,
"prop2": {
"nested": 0
},
"otherprop": "foo"
}
Upvotes: 0
Reputation: 134571
The only safe option you have is to generate the json string manually with the properties ordered in the way you want them. There is no guarantee that the order you set properties on an object be preserved.
You can do so with the help of this function:
def inorder(names): names as $names | . as $obj
| "{\($names | map("\(@json):\($obj[.])") | join(","))}"
;
Then to use it:
inorder(["prop1", "prop3", "prop2"])
Keep in mind that this will return a string. You'll want to get the raw output to get it back as an object.
Upvotes: 0