Home > Software engineering >  JQ - flatten out / unroll double-nested objects with different key names
JQ - flatten out / unroll double-nested objects with different key names

Time:12-14

Having the following json structure:

{
    "outerDescription1": {
        "innerDescription1": {
            "otherProperties": 1,
            "items": [
                "arrayItem1",
                "arrayItem2"
            ]
        }
    },
    "outerDescription2": {
        "innerDescription2": {
            "otherProperties": 2,
            "items": [
                "arrayItem3",
                "arrayItem4"
            ]
        }
    }
}

I would like to get the following result:

{
    "item": "arrayItem1",
    "outer": "outerDescription1",
    "inner": "innerDescription1",
    "otherProperties": 1
}
{
    "item": "arrayItem2",
    "outer": "outerDescription1",
    "inner": "innerDescription1",
    "otherProperties": 1
}
{
    "item": "arrayItem3",
    "outer": "outerDescription2",
    "inner": "innerDescription2",
    "otherProperties": 2
}
{
    "item": "arrayItem4",
    "outer": "outerDescription2",
    "inner": "innerDescription2",
    "otherProperties": 2
}

Assumption: there are many outerDescription and innerDescription keys and they are not known upfront.

Single level unroll is would be simple, but unrolling double-nested objects with different keys is a challenge to me.

The closest what I was able to get was:

jq "with_entries(.value = {outer: .key}   .value)[]"

which resulted in:

{                              
  "outer": "outerDescription1",
  "innerDescription1": {       
    "otherProperties": 1,      
    "items": [                 
      "arrayItem1",            
      "arrayItem2"             
    ]                          
  }                            
}                              
{                              
  "outer": "outerDescription2",
  "innerDescription2": {       
    "otherProperties": 2,      
    "items": [                 
      "arrayItem3",            
      "arrayItem4"             
    ]                          
  }                            
}   

But right now, without knowing the next nested key name exactly, I am not able to unroll for a second time in the same way as outer would be swallowed.

I am using JQ 1.6

CodePudding user response:

You could use path to find the according paths, and getpath to retrieve their values:

path(.[][].items[]) as $p
| {item: getpath($p), outer: $p[0], inner: $p[1]}
  (getpath($p[:-2]) | del(.items))
{
  "item": "arrayItem1",
  "outer": "outerDescription1",
  "inner": "innerDescription1",
  "otherProperties": 1
}
{
  "item": "arrayItem2",
  "outer": "outerDescription1",
  "inner": "innerDescription1",
  "otherProperties": 1
}
{
  "item": "arrayItem3",
  "outer": "outerDescription2",
  "inner": "innerDescription2",
  "otherProperties": 2
}
{
  "item": "arrayItem4",
  "outer": "outerDescription2",
  "inner": "innerDescription2",
  "otherProperties": 2
}

Demo

CodePudding user response:

If you know the name of your items property, the following program is quite readable:

to_entries[]
| {outer: .key, nested: (.value | to_entries[])}
| {item:.nested.value.items[]}
      {outer, inner: .nested.key}
      (.nested.value | del(.items))

Output:

{
  "item": "arrayItem1",
  "outer": "outerDescription1",
  "inner": "innerDescription1",
  "otherProperties": 1
}
{
  "item": "arrayItem2",
  "outer": "outerDescription1",
  "inner": "innerDescription1",
  "otherProperties": 1
}
{
  "item": "arrayItem3",
  "outer": "outerDescription2",
  "inner": "innerDescription2",
  "otherProperties": 2
}
{
  "item": "arrayItem4",
  "outer": "outerDescription2",
  "inner": "innerDescription2",
  "otherProperties": 2
}

CodePudding user response:

Here's a straightforward solution using to_entries:

  to_entries[]
  | .key as $outer
  | .value
  | to_entries[]
  | .key as $inner
  | .value
  | .otherProperties as $otherProperties
  | .items[]
  | {item: ., $outer, $inner, $otherProperties}
  • Related