1

I have a translation management tool to which I upload a json file input.json. Next a translator does his/her translation job and the result is exported to a json file containing all the translations output.json. Both input.json and output.json contain the same keys but not the same values (these are the translations added/modified by the translator). More importantly the order of the keys in input.json and output.json is completely different. Functionally this does not matter but in pull requests the input.json is the "old" file that is compared to the "new" file being output.json. This generates a lot of diffs due to the changed order of the keys, mking reviews cumbersome. Is there an easy way to order output.json in the same way as input.json?

I found that jq could be used to order input.json "alphabetically" so then I could afterwards order output.json alphabetically as well and this would kind of solve my problem but isn't it possible to use jq do actually take the order of the keys in input.json and apply that to output.json?

Here is some demo data for input.json:

{
  "BathroomType": "Type of bathroom",
  "BuildingType": "Type of building",
  "enums": {
    "OrientationGarden": {
      "EAST": "East",
      "SOUTH": "South",
      "NORTH": "North",
      "WEST": "West"
    },
    "TerrainRecentDestination": {
      "EXTRACTION": "extraction",
      "PARK": "park"
    }
  }
}

and for output:

{
  "BathroomType": "Type of bathroom2",
  "enums": {
    "TerrainRecentDestination": {
      "EXTRACTION": "extraction",
      "PARK": "park"
    },
    "OrientationGarden": {
      "SOUTH": "South",
      "EAST": "East2",
      "NORTH": "North",
      "WEST": "West"
    }
  },
  "BuildingType": "Type of building"
}
peak
  • 105,803
  • 17
  • 152
  • 177
Philimmo
  • 11
  • 2
  • IMO, you should not use a translation tool using files if a user has to change it. If data changes I would opt for a DB solution. This has room for easy improvement. Who changed it, when did they change it... Also more important, no changes in your pull requests and no hassle to order your files :) – Wimanicesir May 30 '23 at 08:42
  • A jq-based answer to this kind of question is available at https://stackoverflow.com/questions/76312374/jq-sort-a-json-file-using-another-json-as-order-model – peak May 30 '23 at 09:59

1 Answers1

1

With the current implementation of jq (meaning: this "side-effect" is not documented, it may change without notice), you could predetermine the order of the keys by successively building up your output object. To this end, you would iterate over the input object's keys in the order they are read using keys_unsorted, and set them in the output object, starting out with the empty object {}:

jq 'input as $in | reduce keys_unsorted[] as $key ({}; .[$key] = $in[$key])' \
  input.json output.json > reordered_output.json

(Note that, confusingly, the second input file, here output.json, is referenced by the input filter.)


Edit: It turned out that the data is of a nested structure. While sample data is missing, let's consider all objects as nodes, and every other data type as leafs. In order to access both keys and values, change keys_unsorted to to_entries (which (for now, see above) has the same, desired "side-effect"), and elevate the logic into a named filter (function definition), so that it can be called recursively on objects.

def reorder($in; $ref): $ref | reduce to_entries[] as {$key, $value} ({};
  .[$key] = ($in[$key] | objects |= reorder(.; $value))
);
reorder(input; .)
pmf
  • 24,478
  • 2
  • 22
  • 31
  • Does seem to work for the top level but not for the nested keys – Philimmo May 30 '23 at 13:52
  • @Philimmo Please provide representative and working samples of your files (ideally written out (not linked), and as part of your question), so answerers can adjust their responses accordingly. – pmf May 30 '23 at 14:12
  • so the input file is `{ "BathroomType": "Type of bathroom", "BuildingType": "Type of building", "enums": { "OrientationGarden": { "EAST": "East", "SOUTH": "South", "NORTH": "North", "WEST": "West" }, "TerrainRecentDestination": { "EXTRACTION": "extraction", "PARK": "park" } } }` – Philimmo May 30 '23 at 16:26
  • and the output fie is `{ "BathroomType": "Type of bathroom", "enums": { "TerrainRecentDestination": { "EXTRACTION": "extraction", "PARK": "park" }, "OrientationGarden": { "EAST": "East", "SOUTH": "South", "NORTH": "North", "WEST": "West" } }, "BuildingType": "Type of building" }` – Philimmo May 30 '23 at 16:28
  • not sure how to call the solution from the command line: jq 'input as $in | def reorder($in; $ref): $ref | reduce to_entries[] as {$key, $value} ({}; .[$key] = ($in[$key] | objects |= reorder(.; $value)) ); reorder(input; .)' input.json output.json > reordered_output.json – Philimmo May 30 '23 at 16:33
  • @Philimmo Please provide the samples as part of your question by [editing it](https://stackoverflow.com/posts/76362983/edit). My revised solution is called just the same way, therefore I omitted `jq '…' input.json output.json > reordered_output.json` [Demo](https://jqplay.org/s/DhRZYZz9WB9) (although I see no changes in the values). – pmf May 30 '23 at 16:54