Home > Back-end >  Use JQ to create new object where the key comes from one object and the value comes from another
Use JQ to create new object where the key comes from one object and the value comes from another

Time:03-02

I have the following input:

{
  "Columns": [
    {
      "email": 123,
      "name": 456,
      "firstName": 789,
      "lastName": 450,
      "admin": 900,
      "licensedSheetCreator": 617,
      "groupAdmin": 354,
      "resourceViewer": 804,
      "id": 730,
      "status": 523,
      "sheetCount": 298
    }
  ]
}
{
  "Users": [
    {
      "email": "[email protected]",
      "name": "Abc Def",
      "firstName": "Abc",
      "lastName": "Def",
      "admin": false,
      "licensedSheetCreator": true,
      "groupAdmin": false,
      "resourceViewer": true,
      "id": 521,
      "status": "ACTIVE",
      "sheetCount": 0
    },
    {
      "email": "[email protected]",
      "name": "Aaa Bob",
      "firstName": "Aaa",
      "lastName": "Bob",
      "admin": false,
      "licensedSheetCreator": true,
      "groupAdmin": false,
      "resourceViewer": false,
      "id": 352,
      "status": "ACTIVE",
      "sheetCount": 0
    }
  ]
}

I need to change the key for all key value pairs in users to match the value in Columns, like so:

{
  "Columns": [
    {
      "email": 123,
      "name": 456,
      "firstName": 789,
      "lastName": 450,
      "admin": 900,
      "licensedSheetCreator": 617,
      "groupAdmin": 354,
      "resourceViewer": 804,
      "id": 730,
      "status": 523,
      "sheetCount": 298
    }
  ]
}
{
  "Users": [
    {
      123: "[email protected]",
      456: "Abc Def",
      789: "Abc",
      450: "Def",
      900: false,
      617: true,
      354: false,
      804: true,
      730: 521,
      523: "ACTIVE",
      298: 0
    },
    {
      123: "[email protected]",
      456: "Aaa Bob",
      789: "Aaa",
      450: "Bob",
      900: false,
      617: true,
      354: false,
      804: false,
      730: 352,
      523: "ACTIVE",
      298: 0
    }
  ]
}

I don't mind if I update the Users array or create a new array of objects. I have tried several combinations of with entries, to entries, from entries, trying to search for keys using variables but the more I dive into it, the more confused I get.

CodePudding user response:

Elements of a stream are processed independently. So we have to change the input.

We could group the stream elements into an array. For an input stream, this can be achieved using --slurp/-s.[1]

jq -s '
   ( .[0].Columns[0] | map_values( tostring ) ) as $map |
   (
      .[0],
      (
         .[1:][] |
         .Users[] |= with_entries(
            .key = $map[ .key ]
         )
      )
   )
'

Demo on jqplay

Alternatively, we could use --null-input/-n in conjunction with input and/or inputs to read the input.

jq -n '
   input |
   ( .Columns[0] | map_values( tostring ) ) as $map |
   (
      .,
      (
         inputs |
         .Users[] |= with_entries(
            .key = $map[ .key ]
         )
      )
   )
'

Demo on jqplay

Note that your desired output isn't valid JSON. Object keys must be strings. So the above produces a slightly different document than requested.

Note that I assumed that .Columns is always an array of one exactly one element. This is a nonsense assumption, but it's the only way the question makes sense.


  1. For a stream the code generates, you could place the stream generator in an array constructor ([]). reduce can also be used to collect from a stream. For example, map( ... ) can be written as [ .[] | ... ] and as reduce .[] as $_ ( []; . [ $_ | ... ] ).

CodePudding user response:

The following has the merit of simplicity, though it does not sort the keys. It assumes jq is invoked with the -n option and of course produces a stream of valid JSON objects:

input
| . as $Columns
| .Columns[0] as $dict
| input # Users
| .Users[] |= with_entries(.key |= ($dict[.]|tostring))
| $Columns, .

If having the keys sorted is important, then you could easily add suitable code to do that; alternatively, if you don't mind having the keys of all objects sorted, you could use the -S command-line option.

  • Related