Search code examples
jq

jq: Calculating a new property in local context nested within identity transform


Example JSON with many properties at various levels elided:

{
  "partitiontable": {
    "label": "gpt",
    "partitions": [
      {
        "node": "/dev/nvme0n1p1",
        "start": 2048,
        "size": 204801
      },
      {
        "node": "/dev/nvme0n1p2",
        "start": 208896,
        "size": 4097
      },
      {
        "node": "/dev/nvme0n1p3",
        "start": 243712,
        "size": 65501185
      },
      {
        "node": "/dev/nvme0n1p4",
        "start": 65810432,
        "size": 3841118208
      }
    ]
  }
}

within each element of the 'partitions' array, I'd like to perform the property-creation operation: .end = .start + .size resulting in a document such as:

{
  "partitiontable": {
    "label": "gpt",
    "partitions": [
      {
        "end": 206849,
        "node": "/dev/nvme0n1p1",
        "start": 2048,
        "size": 204801
      },
      {
        "end": 212993,
        "node": "/dev/nvme0n1p2",
        "start": 208896,
        "size": 4097
      },
      {
        "end": 65744897,
        "node": "/dev/nvme0n1p3",
        "start": 243712,
        "size": 65501185
      },
      {
        "end": 3906928640,
        "node": "/dev/nvme0n1p4",
        "start": 65810432,
        "size": 3841118208
      }
    ]
  }
}

There could be many other properties within each object that I'll need to preserve (hopefully without explicit enumeration).

I've attempted straight-out .partitiontable.partitions[].end = .size + .start, but those 'end' properties wind up null, I'm presuming because there's no 'context'. I've also tried using the '..' operator to recurse, but I only get the local-scoping object as output. (jq '.. | objects | select(.start) | .end = .start + .size' results in the array objects but loses the outer document.)


Solution

  • The update operation |= is an assignment with the context of the LHS promoted to the RHS. With that, .start, .size, and .end can be resolved correctly.

    jq '.partitiontable.partitions[] |= (.end = .start + .size)'
    
    {
      "partitiontable": {
        "label": "gpt",
        "partitions": [
          {
            "node": "/dev/nvme0n1p1",
            "start": 2048,
            "size": 204801,
            "end": 206849
          },
          {
            "node": "/dev/nvme0n1p2",
            "start": 208896,
            "size": 4097,
            "end": 212993
          },
          {
            "node": "/dev/nvme0n1p3",
            "start": 243712,
            "size": 65501185,
            "end": 65744897
          },
          {
            "node": "/dev/nvme0n1p4",
            "start": 65810432,
            "size": 3841118208,
            "end": 3906928640
          }
        ]
      }
    }
    

    Demo