Andy
Andy

Reputation: 8091

Use jq to merge keys with common id

consider a file 'b.json':

[
  {
    "id": 3,
    "foo": "cannot be replaced, id isn't in a.json, stay untouched",
    "baz": "do not touch3"
  },
  {
    "id": 2,
    "foo": "should be replaced with 'foo new2'",
    "baz": "do not touch2"
  }
]

and 'a.json':

[
  {
    "id": 2,
    "foo": "foo new2",
    "baz": "don't care"
  }
]

I want to update the key "foo" in b.json using jq with the matching value from a.json. It should also work with more than one entry in a.json.

Thus the desired output is:

[
  {
    "id": 3,
    "foo": "cannot be replaced, id isn't in a.json, stay untouched",
    "baz": "do not touch3"
  },
  {
    "id": 2,
    "foo": "foo new2",
    "baz": "do not touch2"
  }
]

Upvotes: 1

Views: 520

Answers (2)

peak
peak

Reputation: 116750

Here's one of several possibilities that use INDEX/2. If your jq does not have this as a built-in, see below.

jq --argfile a a.json '
  INDEX($a[]; .id) as $dict
  | map( (.id|tostring) as $id
         | if ($dict|has($id)) then .foo = $dict[$id].foo 
           else . end)' b.json

There are other ways to pass in the contents of a.json and b.json.

Caveat

The above use of INDEX assumes there are no "collisions", which would happen if, for example, one of the objects has .id equal to 1 and another has .id equal to "1". If there is a possibility of such a collision, then a more complex definition of INDEX could be used.

INDEX/2

Straight from builtin.jq:

def INDEX(stream; idx_expr):
  reduce stream as $row ({}; .[$row|idx_expr|tostring] = $row);

Upvotes: 2

peak
peak

Reputation: 116750

Here's a generic answer that makes no assumptions about the values of the .id keys except that they are distinct JSON values.

Generalization of INDEX/2

def type2: [type, if type == "string" then . else tojson end];

def dictionary(stream; f):
  reduce stream as $s ({}; setpath($s|f|type2; $s));

def lookup(value):
  getpath(value|type2);

def indictionary(value):
  (value|type2) as $t
  | has($t[0]) and (.[$t[0]] | has($t[1]));

Invocation

jq --argfile a a.json -f program.jq b.json 

main

dictionary($a[]; .id) as $dict
| b
| map( .id as $id 
       | if ($dict|indictionary($id)) 
         then .foo = ($dict|lookup($id).foo) 
         else . end)

Upvotes: 1

Related Questions