EBAH
EBAH

Reputation: 99

How to filter object at multiple levels in JQ?

I have a json like this but much longer:

[
  {
    "id": "123",
    "name": "home network configuration",
    "description": "home utilities",
    "definedRanges": [
      {
        "id": "6500b67e",
        "name": "100-200",
        "beginIPv4Address": "192.168.090.100",
        "endIPv4Address": "192.168.090.200",
        "state": "UNALLOCATED"
      }
    ]
  },
  {
    "id": "456",
    "name": "lab network configuration",
    "description": "lab experiments",
    "definedRanges": [
      {
        "id": "1209b90d",
        "name": "100-200",
        "beginIPv4Address": "192.168.090.100",
        "endIPv4Address": "192.168.090.200",
        "state": "ALLOCATED"
      },
      {
        "id": "99e08ca4",
        "name": "100-200",
        "beginIPv4Address": "192.168.090.100",
        "endIPv4Address": "192.168.090.200",
        "state": "UNALLOCATED"
      }
    ]
  }
]

I'd like to query with jq and obtain the following:

[
  {
    "name": "home network configuration"
    "definedRanges": [
      {
        "name": "100-200",
        "beginIPv4Address": "192.168.090.100",
        "endIPv4Address": "192.168.090.200",
      }
    ]
  },
  {
    "name": "lab network configuration",
    "definedRanges": [
      {
        "name": "100-200",
        "beginIPv4Address": "192.168.090.100",
        "endIPv4Address": "192.168.090.200",
      },
      {
        "name": "100-200",
        "beginIPv4Address": "192.168.090.100",
        "endIPv4Address": "192.168.090.200",
      }
    ]
  }
]

or even this:

[
  {
    "name": "home network configuration",
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200",
  },
  {
    "name": "lab network configuration",
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200",
  },
  {
    "name": "lab network configuration",
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200",
  }
]

So far I was able to extract the network name at the first level with:

.[] | {name}

I could also extract the definedRanges with:

.[].definedRanges[] | {name,beginIPv4Address,endIPv4Address}

But I can't figure out how to merge the two with jq.

I solved the problem with a very simple python script (7 lines of code) but now I'd like to understand how to do the same with jq, out of curiosity.

Upvotes: 0

Views: 238

Answers (3)

knittl
knittl

Reputation: 265221

Here's my shot at solutions to produce one or the other desired output:

map(
  { name }
  + (.definedRanges[] | {
      "definedRanges.name": .name,
      "definedRanges.beginIPv4Address": .beginIPv4Address,
      "definedRanges.endIPv4Address": .endIPv4Address
  }))

Output:

[
  {
    "name": "home network configuration",
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200"
  },
  {
    "name": "lab network configuration",
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200"
  },
  {
    "name": "lab network configuration",
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200"
  }
]

Producing the first kind of output is even simpler (IMHO it reads a bit more straightforward):

map({
    name,
    definedRanges: .definedRanges | map({ name, beginIPv4Address, endIPv4Address })
})

Output:

[
  {
    "name": "home network configuration",
    "definedRanges": [
      {
        "name": "100-2001",
        "beginIPv4Address": "192.168.090.101",
        "endIPv4Address": "192.168.090.201"
      }
    ]
  },
  {
    "name": "lab network configuration",
    "definedRanges": [
      {
        "name": "100-2002",
        "beginIPv4Address": "192.168.090.102",
        "endIPv4Address": "192.168.090.202"
      },
      {
        "name": "100-2003",
        "beginIPv4Address": "192.168.090.103",
        "endIPv4Address": "192.168.090.203"
      }
    ]
  }
]

Upvotes: 0

0stone0
0stone0

Reputation: 43972

The 'or even this' can be achieved using:

map(.name as $name | .definedRanges[] | { name, beginIPv4Address, endIPv4Address} | with_entries(.key = "definedRanges." + .key) | .name = $name)

Which yields:

[
  {
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200",
    "name": "home network configuration"
  },
  {
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200",
    "name": "lab network configuration"
  },
  {
    "definedRanges.name": "100-200",
    "definedRanges.beginIPv4Address": "192.168.090.100",
    "definedRanges.endIPv4Address": "192.168.090.200",
    "name": "lab network configuration"
  }
]

Demo

Upvotes: 1

oguz ismail
oguz ismail

Reputation: 50750

Well, you were close. Here's how you put those together:

map({name, definedRanges: .definedRanges | map({name, beginIPv4Address, endIPv4Address})})

Online demo

Upvotes: 1

Related Questions