A.H.
A.H.

Reputation: 66263

Handling optional entries

I'd like to transform the priceDimension parts in the following JSON

{
  "priceDimensions": {
    "222FSQ7X57S9DN65.NQ3QZPMQV9.2TG2D8R56U": {
      "unit": "Quantity",
      "description": "Upfront Fee",
      "USD": "493999"
    },
    "222FSQ7X57S9DN65.NQ3QZPMQV9.6YS6EN2CT7": {
      "unit": "Hrs",
      "description": "...",
      "USD": "0.0000000000"
    }
  },
  "foo": "bar",
  "bla": "blub"
}

into something like this:

{
  "priceDimensions": {
    "ufrontFee": "493999",
    "hourFee": "0.0000000000"
  },
  "foo": "bar",
  "bla": "blub"
}

The following script works if both priceDimension entries are present:

.priceDimensions |=
{
    ufrontFee: .[] | select(.unit == "Quantity").USD,
    hourFee:   .[] | select(.unit == "Hrs").USD
}

But both entries are in fact optional.

If one is missing then I assumed that only the corresponding key/value has a null value (e.g. "upFrontFee": null). But in fact the complete result is null. Why is that and - of course - how to avoid that efficiently?

Ultimately the key/value for a missing priceDimension should be omitted alltogetger as shown below. How to do that?

{
  "priceDimensions": {
    "hourFee": "0.0000000000"
  },
  "foo": "bar",
  "bla": "blub"
}

Edit I have noted that jq-1.5 behaves differently than jq-1.6: After replacing Quantity with QuantityX in the example input jq-1.5 outputs:

null

but jq-1.6 (see https://jqplay.org/s/XVgl5UCDLM) outputs

{
  "foo": "bar",
  "bla": "blub"
}

Upvotes: 2

Views: 86

Answers (3)

A.H.
A.H.

Reputation: 66263

I have found this solution:

.priceDimensions |=
  ({ ufrontFee: .[] | select(.unit == "Quantity").USD } // null)
  +
  ({ hourFee:   .[] | select(.unit == "Hrs").USD } // null)

Using a custom function I can write it a little bit shorter:

def optional_cost($k; $cmp):
    { ($k): .[] | select(.unit == $cmp).USD } // null;
    
.priceDimensions |=
    optional_cost("ufrontFee"; "Quantity")
    + optional_cost("hourFee"; "Hrs")

Upvotes: 0

peak
peak

Reputation: 116870

Keeping it short and sweet:

.priceDimensions |=
  ( (first(.[] | select(.description == "Upfront Fee").USD) // null) as $uff
    | (first(.[] | select(.unit == "Hrs").USD) // null) as $hf
    | if $uff then {ufrontFee: $uff} else null end
      + if $hf then {hourFee: $hf} else null end)

You might want to tweak this to meet your expectations if neither key is present.

This has been tested with jq 1.5 and jq 1.6. For earlier versions of jq, you would have to drop the calls to first, which are there only for robustness anyway.

Upvotes: 0

Jagadesh
Jagadesh

Reputation: 2116

Try this,

.priceDimensions |= {
     ufrontFee: .[] | select(.unit == "Quantity").USD,
     hourFee:   .[] | select(.unit == "Hrs").USD
} | delpaths([path(..?) as $p | select(getpath($p) == null) | $p])

Upvotes: 1

Related Questions