paper_trees
paper_trees

Reputation: 11

JSON/JQ: Merge 2 files on key-value with condition

I have 2 JSON files. I would like to use jq to take the value of "capital" from File 2 and merge it with File 1 for each element where the same "name"-value pair occurs. Otherwise, the element from File 2 should not occur in the output. If there is no "name"-value pair for an element in File 1, it should have empty text for "capital."

File 1:

{
   "countries":[
      {
         "name":"china",
         "continent":"asia"
      },
      {
         "name":"france",
         "continent":"europe"
      }
   ]
}

File 2:

{
   "countries":[
      {
         "name":"china",
         "capital":"beijing"
      },
      {
         "name":"argentina",
         "capital":"buenos aires"
      }
   ]
}

Desired result:

{
   "countries":[
      {
         "name":"china",
         "continent":"asia",
         "capital":"beijing"
      },
      {
         "name":"france",
         "continent":"europe",
         "capital":""
      }
   ]
}

Upvotes: 1

Views: 560

Answers (2)

peak
peak

Reputation: 116740

You could first construct a dictionary from File2, and then perform the update, e.g. like so:

jq --argfile dict File2.json '
  ($dict.countries | map( {(.name): .capital}) | add) as $capitals
  | .countries |= map( .capital = ($capitals[.name] // ""))
' File2.json 

From a JSON-esque perspective, it would probably be better to use null for missing values; in that case, you could simplify the above by omitting // "".

Using INDEX/2

If your jq has INDEX/2, then the $capitals dictionary could be constructed using the expression:

INDEX($dict.countries[]; .name) | map_values(.capital)

Using INDEX makes the intention clearer, but if efficiency were a major concern, you'd probably be better off using reduce explicitly:

reduce $dict.countries[] as $c ({}; . + ($c | {(.name): .capital}))

Upvotes: 1

Shawn
Shawn

Reputation: 52364

One way:

$ jq --slurpfile file2 file2.json '
   { countries:
      [ .countries[] |
        . as $curr |
        $curr + { capital: (($file2[0].countries[] | select(.name == $curr.name) | .capital) // "") }
      ]
    }' file1.json
{
  "countries": [
    {
      "name": "china",
      "continent": "asia",
      "capital": "beijing"
    },
    {
      "name": "france",
      "continent": "europe",
      "capital": ""
    }
  ]
}

An alternative:

 $ jq -n '{ countries: ([inputs] | map(.countries) | flatten | group_by(.name) |
                        map(select(.[] | has("continent")) | add | .capital //= ""))
          }' file[12].json

Upvotes: 0

Related Questions