0

I have below json file, need to replace tbd- with premium- in task's value if and only if made is german and task's value starts with tbd-

{
    "vehicle": {
        "maintenance": [
            {
                "parts": "wheel",
                "size": ["one", "two"]
            },
            {
                "task": "tbd-service-oil",
                "car": {
                    "german": {
                        "audi": ["Synthetic"]
                    }
                },
                "some": ["other"]
            },
            {
                "task": "service-oil",
                "honda": {
                    "japan": {
                        "oil": ["regular"]
                    }
                }
            }
        ],
        "repair": [
            {
                "parts": "wheel",
                "size": ["one", "two"]
            },
            {
                "task": "tbd-engine-repair",
                "car": {
                    "german": {
                        "engine": ["6-cyl"]
                    }
                }
            },
            {
                "task": "engine-repair",
                "car": {
                    "german": {
                        "engine": ["4-cyl"]
                    }
                }
            }
        ]
        
    }
}

need to update above json file to:

{
    "vehicle": {
        "maintenance": [
            {
                "parts": "wheel",
                "size": ["one", "two"]
            },
            {
                "task": "premium-service-oil", ## update this b'cos there is "german" under "car" and task's value had prefix "tbd-"
                "car": {
                    "german": {
                        "audi": ["Synthetic"]
                    }
                },
                "some": ["other"]
            },
            {
                "task": "service-oil",
                "honda": {
                    "japan": {
                        "oil": ["regular"]
                    }
                }
            }
        ],
        "repair": [
            {
                "parts": "wheel",
                "size": ["one", "two"]
            },
            {
                "task": "premium-engine-repair", ## update this b'cos there is "german" under "car" and task's value had prefix "tbd-"
                "car": {
                    "german": {
                        "engine": ["6-cyl"]
                    }
                }
            },
            {
                "task": "engine-repair", ### no need to update this as it don't have "tbd-" as prefix
                "car": {
                    "german": {
                        "engine": ["4-cyl"]
                    }
                }
            }
        ]
        
    }
}

so far, I tried to get all the keys with german as keyname, I am not successful though

jq -c 'to_entries[] | select (.key.maintenance.car == "german") | [.key]' json
jq: error (at json:50): Cannot index string with string "maintenance"

I could query the parts matching wheel, using similar command

$ jq -c 'to_entries[] | select (.value.maintenance[0].parts == "wheel") | [.key]'  json
["vehicle"]

UPDATE:

I am okay to skip checking if key has tbd-, I can go update all the key names irrespective of prefix.

rodee
  • 3,233
  • 5
  • 34
  • 70
  • 3
    Welcome to Stack Overflow. [SO is a question and answer page for professional and enthusiast programmers](https://stackoverflow.com/tour). Please add your own code to your question. You are expected to show at least the amount of research you have put into solving this question yourself – Cyrus Sep 30 '20 at 19:45
  • https://stackoverflow.com/questions/18592173/select-objects-based-on-value-of-variable-in-object-using-jq this was the closest I could find, I am reading through `jq` documentation, but it's being very hard for me to grasp, just started with jq. – rodee Sep 30 '20 at 19:51

2 Answers2

2

Here's a solution using walk. If for some reason you want a more targeted solution (for example, one that does not use walk), it should be easy to modify it accordingly.

walk( if type=="object" and .task and (.task|startswith("tbd-")) and
         any(.[]; type=="object" and has("german"))
      then .task|=sub("tbd-"; "premium-")
      else . end )
peak
  • 105,803
  • 17
  • 152
  • 177
  • Thanks, but I cannot use walk as its not installed on all servers, jq is installed on all. – rodee Oct 01 '20 at 16:25
  • @rodee - Simply prepend the def of walk to the jq program. You can do that even if jq already defines it. – peak Oct 01 '20 at 16:28
  • will you be able to help with https://stackoverflow.com/q/64213506/2748513 thank you. – rodee Oct 09 '20 at 13:21
1

If you don't have jq 1.6 , which has walk/1 , do prepend the def walk :

jq '
# Apply f to composite entities recursively, and to atoms
    def walk(f):
    . as $in
    | if type == "object" then
        reduce keys[] as $key
            ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
    elif type == "array" then map( walk(f) ) | f
    else f
    end;
  walk( if type=="object" and .task and (.task|startswith("tbd-")) and any(.[]; type=="object" and has("german")) then .task|=sub("tbd-"; "premium-") else . end )
' filename

source: https://github.com/stedolan/jq/issues/963#issuecomment-152783116

Fábio Almeida
  • 189
  • 2
  • 8