Reputation: 21
Using jq I need to delete a specific key:value pair if value contains string "null". The string could be " null" or "null ". Hence need to use contains and not exact string match. If not a string it will be a number
My sample json is as below: (The null value is expected only in the 'th' and 'sf' keys)
'dets':{
'S1':{
'type':'class',
'input': [12,7,6,19],
'config':{
'file':'sfile1',
'th': -10,
'sf': 'null'
}
},
'S2':{
'type':'class',
'input': [12,7,6,19],
'config':{
'file':'sfile2',
'th': -5,
'sf': 3
}
},
'S3':{
'type':'bottom',
'input': [12,7,16],
'config':{
'file':'sfile3',
'th': ' null',
'sf': 'null '
}
}
}
The required output should be like:
'dets':{
'S1':{
'type':'class',
'input': [12,7,6,19],
'config':{
'file':'sfile1',
'th': -10
}
},
'S2':{
'type':'class',
'input': [12,7,6,19],
'config':{
'file':'sfile2',
'th': -5,
'sf': 3
}
},
'S3':{
'type':'bottom',
'input': [12,7,16],
'config':{
'file':'sfile3'
}
}
}
I believe something on the lines of del(.[][].config.smoothing_span | select(contains("null"))) but i am running into a problem since type is different.
Upvotes: 1
Views: 2161
Reputation: 116880
The given data is not valid JSON but it is valid HJSON, so the first step in the following jq-oriented solution is to use hjson to convert the data to JSON:
hjson -j input.hjson
The concept of what values should be regarded as "null" might change over time, so in the following let's define a filter that can be used to capture whichever definition is appropriate, e.g.
def isnull: . == null or (type=="string" and test("null"));
(Perhaps a better definition would use test("^ *null *$")
.)
isnull
, you could use walk/1
:
walk(if type=="object"
then with_entries(select(.value|isnull|not))
else . end)
(If your jq does not have walk, you could simply copy-and-paste its definition from https://github.com/stedolan/jq/blob/master/src/builtin.jq or elsewhere on the web.)
Assuming your jq has walk
, we could therefore write:
hjson -j input.hjson |
jq 'def isnull: . == null or (type=="string" and test("null"));
walk(if type=="object"
then with_entries(select(.value|isnull|not))
else . end)'
If you want the scope of the changes to be as narrow as possible, you could use map_values
, e.g. in conjunction with a helper function for checking and possibly deleting a specific key:
# key is assumed to be a filter, e.g. .foo
def delkey(key): if key | isnull then del(key) else . end;
.dets |= map_values(.config |= (delkey(.th) | delkey(.sf)))
If there are several specific keys to be checked, it might be more convenient to define a function for checking a list of keys:
# keys is assumed to be an array of strings
def delkeys(keys):
with_entries(select(.key as $k
| (keys|index($k)) and (.value|isnull) | not));
.dets |= map_values(.config |= delkeys(["th", "sf"]))
Upvotes: 3