1

I would like to change a field in my json file as specified by another json file. My input file is something like:

{"id": 10, "name": "foo", "some_other_field": "value 1"}
{"id": 20, "name": "bar", "some_other_field": "value 2"}
{"id": 25, "name": "baz", "some_other_field": "value 10"}

I have an external override file that specifies how name in certain objects should be overridden, for example:

{"id": 20, "name": "Bar"}
{"id": 10, "name": "foo edited"}

As shown above, the override may be shorter than input, in which case the name should be unchanged. Both files can easily fit into available memory.

Given the above input and the override, I would like to obtain the following output:

{"id": 10, "name": "foo edited", "some_other_field": "value 1"}
{"id": 20, "name": "Bar", "some_other_field": "value 2"}
{"id": 25, "name": "baz", "some_other_field": "value 10"}

Being a beginner with jq, I wasn't really sure where to start. While there are some questions that cover similar ground (the closest being this one), I couldn't figure out how to apply the solutions to my case.

user4815162342
  • 141,790
  • 18
  • 296
  • 355

1 Answers1

4

There are many possibilities, but probably the simplest, efficient solution would use the built-in function: INDEX/2, e.g. as follows:

jq -n --slurpfile dict f2.json '
  (INDEX($dict[]; .id) | map_values(.name)) as $d
  | inputs
  | .name = ($d[.id|tostring] // .name)
' f1.json

This uses inputs with the -n option to read the first file so that each JSON object can be processed in turn.

Since the solution is so short, it should be easy enough to figure it out with the aid of the online jq manual.

Caveat

This solution comes with a caveat: that there are no "collisions" between ids in the dictionary as a result of the use of "tostring" (e.g. if {"id": 10} and {"id": "10"} both occurred).

If the dictionary does or might have such collisions, then the above solution can be tweaked accordingly, but it is a bit tricky.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
peak
  • 105,803
  • 17
  • 152
  • 177
  • Works like a charm, thanks! Indeed there are no collisions, as all IDs are numeric. I'm now studying _how_ it works - as you say, it's short and "obvious" once written, but would (for me) be quite difficult to come up with. – user4815162342 Feb 22 '21 at 19:26
  • I've now noticed that this is outputting `name: null` instead of `name: "baz"` for id==25. I've edited the answer to fix the issue; feel free to reject the edit if there is a better way to do it. – user4815162342 Feb 22 '21 at 19:56
  • 1
    @user4815162342 - Perfect! – peak Feb 22 '21 at 20:14