Reputation: 2140
I have this input JSON
[{
"Name":"Wolfenstein",
"24 Hour":"FALSE",
"Shop 1":"TRUE",
"Shop 2":"FALSE",
}]
and want to change all "FALSE" and "TRUE" values to false
and true
respectively.
[{
"Name":"Wolfenstein",
"24 Hour":false,
"Shop 1":true,
"Shop 2":false,
}]
As a bonus, I only want to select those array items whose keys start with "Shop" and have at least on "Shop" set to "TRUE". What would be the jq filter to use?
Upvotes: 0
Views: 3618
Reputation: 26270
The following code will replace string "TRUE" with Boolean true
and you can pipe that to a similar expression for "FALSE":
.[][] |= if . == "TRUE" then true end
To select the items that have keys starting with "Shop" and have at least one of their value set to true
you can pipe the result to:
map(select(with_entries(select((.key | startswith("Shop")) and .value))!={}))
Or all combined:
.[][] |= if . == "TRUE" then true end
| .[][] |= if . == "FALSE" then false end
| map(select(with_entries(select((.key | startswith("Shop")) and .value))!={}))
The inner select
here selects only those properties from the object, with key starting with "Shop" and with truthy value. We are assuming here, that you do not have values other than "TRUE" and "FALSE" for these properties to start with. If any such properties found the resulting object with_entries
operated on will not be empty, so we can use that as the condition for the outer select
- this will result in objects that we are not interested in being skipped. Finally, map
which is a synonym for [.[]|f]
is used so that we get a proper json array back, and not a stream of multiple json objects.
Upvotes: 0
Reputation: 116690
Your overall requirements are unclear, but you might want to consider using walk
if you really want to update an arbitrary JSON text in the manner you suggest. For simplicity, though, I'll assume you simply have an array of objects of the kind you show.
Let's start with the simple task of changing TRUE/FALSE to true/false. This could be accomplished directly as follows:
map( map_values(if . == "TRUE" then true
elif . == "FALSE" then false
else .
end) )
But because of your 'at least one "Shop" set to "TRUE"' requirement, it will be helpful to define an auxiliary function:
def toboolean:
if . == "TRUE" then true
elif . == "FALSE" then false
else .
end;
So the first task can be accomplished by:
map(map_values(toboolean))
Now we're good to go. Assuming your jq has any/2
, and under one interpretation of your overall requirements, we could write:
map( if any( to_entries[];
(.key|startswith("Shop")) and .value=="TRUE" )
then map_values(toboolean)
else .
end)
Or if only the "Shop" values are to be altered:
map( if any( to_entries[];
(.key|startswith("Shop")) and .value=="TRUE" )
then with_entries( if .key|startswith("Shop")
then .value |= toboolean
else .
end)
else .
end)
If your jq does not have any/2
, then please consider upgrading; if that is not an option, you could write your own (inefficient) version using reduce
.
The solutions above can be streamlined using the generic function when/2
, defined as:
def when(filter; action): if filter//null then action else . end;
For example, the eight-line solution above becomes this four-liner, which might also be easier to read once one becomes familiar with when/2
:
map( when( any( to_entries[];
(.key|startswith("Shop")) and .value=="TRUE" );
with_entries( when( .key|startswith("Shop");
.value |= toboolean) ) ))
Upvotes: 2