skip to Main Content

I have a json array of hundreds of objects and each object contains (among many other fields) two arrays like this and a status indicator:

"searchMetaData": {"rrStandard": ["XYZ5.1","6.3"],"ccStandard": ["46d45a68-a930","8cd1dc9a-d6a2"],"status": "complete"}

For any object where status is "complete", I want to look at the rrStandard array and remove any array elements that don’t start with XYZ (ie, it would remove "6.3"). But I also need to remove the corresponding element in the ccStandard array, but that array doesn’t have patterns I can match on, so I think I would have to remove based on a corresponding index number.

Is there a way with jq to remove the "6.3" from rrStandard, somehow remember what its index was (e.g. 1) and then remove the element at that same index (e.g. index 1) in the ccStandard array as well?

In the production case, these arrays will sometimes have more than just 2 values in the arrays, but the number of elements will match between the two. So, it may be that I need to remove elements 1,2, and 3 across both arrays for example.

I’m new to jq and have (I think) figured out how to find and remove the "6.3" from rrStandard but from there I’m lost.

Here is what I have so far to simply find and delete the "6.3" from rrStandard:

.[].searchMetaData |= if(.status == "complete") then (del(.rrStandard[] | select(startswith("XYZ") | not))) else . end

2

Answers


  1. One way would be to use to_entries to get an array of indices and values, filter for the values and use the indices to remove from both arrays:

    del(.[].searchMetaData | select(.status == "complete") | (
      .rrStandard, .ccStandard
    )[
      .rrStandard | to_entries[] | select(.value | startswith("XYZ") | not).key
    ])
    

    Demo

    Another way would be to reduce over the keys in one array, and delete from both arrays if there is a match. The iteration is in reverse order to not affect higher indices after lower ones have been deleted:

    (.[].searchMetaData | select(.status == "complete")) |= reduce (
      .rrStandard | keys | reverse[]
    ) as $i (.;
      del((.rrStandard, .ccStandard)[
        select(.rrStandard[$i] | startswith("XYZ") | not) | $i
      ])
    )
    

    Demo

    Yet another way would be to coordinate both arrays and transpose them twice. After the first one, the elements line up, and those matching can be deleted in one go:

    (.[].searchMetaData | select(.status == "complete")) |= . + (
      [
        [.rrStandard, .ccStandard] | transpose[]
        | select(first | startswith("XYZ"))
      ]
      | transpose | {rrStandard: first, ccStandard: last}
    )
    

    Demo

    Login or Signup to reply.
  2. You’re looking for something like this:

    (.[].searchMetaData | select(.status == "complete")) |=
    [.rrStandard | path (.[] | select(startswith("XYZ") | not))] as $p
    | (.rrStandard, .ccStandard) |= delpaths($p)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search