Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

🍿 Pick values in nested structures with jq

Snacks (🍿) are my collection of recipes for solving problems. Often recorded (and cleaned up) from actual discussions where I'm involved in helping others with technical problems. Not all solutions are mine but I started collecting them in one place because you never know when you need one.


Let's say you have a localization JSON like this:

{
  "languages": {
    "fi": "Suomi",
    "en": "English",
    "sv": "Svenska"
  },
  "send": {
    "en": "Send",
    "fi": "LÀhetÀ",
    "sv": "SĂ€nd"
  },
  "ordinals": {
    "1": {
      "en": "first",
      "fi": "EnsimmÀinen",
      "sv": "första"
    },
    "2": {
      "en": "second",
      "fi": "toinen",
      "sv": "andra"
    }
  },
  "age": {
    "en": "Age",
    "fi": "IkÀ",
    "sv": "Ålder"
  }
}

For some use case, you'd like to get only the Finnish values out from the nested structure, so the outcome would look like:

{
  "languages": "Suomi",
  "send": "LÀhetÀ",
  "ordinals": {
    "1": "EnsimmÀinen",
    "2": "toinen"
  },
  "age": "IkÀ"
}

jq is a command line tool to parse and modify JSON objects. It's syntax is compact, declarative, powerful and quite difficult to grasp because of those other attributes.

Given that our JSON is in a file called input.json, we can run:

jq 'walk(if type == "object" and has("fi") then .fi else . end)' < input.json

Let's walk through what happens here:

  1. walk is a function that recursively walks through every element in the JSON.
  2. for each element, we check if it's an "object" type and has a key "fi".
  3. if it does, we replace it with the value .fi which means "the value from key 'fi'"
  4. if it doesn't, we replace it with the value . which means "the current value"

Other jq snacks: