0

I'm trying to parse and mofidy JSON with Groovy. Source JSON from REST API looks like:

[
   {
      "id":27858,
      "type":"ad",
      "stats":[
         {
            "day":"2021-01-21",
            "sex":[
               {
                  "impressions_rate":0.349,
                  "value":"f"
               },
               {
                  "impressions_rate":0.621,
                  "value":"m",
                  "clicks_rate":0.22
               }
            ],
            "age":[
               {
                  "impressions_rate":0.217,
                  "value":"18-21"
               }
            ]
         },
         {
            "day":"2021-02-25",
            "sex":[
               {
                  "impressions_rate":0.349,
                  "value":"f"
               },
               {
                  "impressions_rate":0.651,
                  "value":"m"
               }
            ],
            "age":[
               {
                  "impressions_rate":0.217,
                  "value":"18-21"
               }
            ]
         }
      ]
   },
   {
      "id":565291,
      "type":"ad",
      "stats":[
         {
            "day":"2021-03-21",
            "sex":[
               {
                  "impressions_rate":0.78,
                  "value":"f",
                  "clicks_rate":0.33
               },
               {
                  "impressions_rate":0.551,
                  "value":"m"
               }
            ],
            "age":[
               {
                  "impressions_rate":0.17,
                  "value":"18-21"
               }
            ]
         }
      ]
   }
]

It's an array with some ids and data for them. I want to grab id, day inside stats array and elements from sex array. After all manipulations my JSON should be like this:

[
  {
    "id": 27858,
    "day": "2021-01-21",
    "impression_rate": 0.349,
    "value": "f"
  },
  {
    "id": 27858,
    "day": "2021-01-21",
    "impression_rate": 0.621,
    "value": "f",
    "clicks_rate": 0.22
  },
  {
    "id": 27858,
    "day": "2021-02-25",
    "impressions_rate":0.349,
    "value":"f"
  },
  {
    "id": 27858,
    "day": "2021-02-25",
    "impressions_rate":0.651,
    "value":"m"
  },
  {
    "id": 565291,
    "day": "2021-03-21",
    "impressions_rate":0.78,
    "value":"f",
    "clicks_rate":0.33
  },
  {
    "id": 565291,
    "day": "2021-03-21",
    "impressions_rate":0.78,
    "value":"f",
    "clicks_rate":0.33
  }
]

So, the main goal is - loop through all ids -> elements in sex array (for each id) and add to these elements day and id mapped fields. I tried to start with empty map with inject, but after 1 hour of debugging i still can't achieve desired output, so maybe better to loop through existed values in array? But I can't even reach sex array.

import groovy.json.*

def json = new JsonSlurper().parseText '''...'''
List expected = json.inject([]){ r, e ->

    Map ids = e.findAll {
        k, v -> k == "id"
    }

    e.each{ k, v ->
    if( (v.any{ it.sex } ) 
        v.each{ r << ids + it }
    }
    return r
}

1 Answer 1

3

If you have nested structures, that contain again nested structures, a good option, to get a flat result, is to use collectMany; like collect it transforms each item of the iterated container, but the results gets concated.

E.g. you can collectMany on your outer data, then again on the stats, and finally just collect over sex.

def data = new groovy.json.JsonSlurper().parse("data.json" as File)

println data.collectMany{ o ->
    o.stats.collectMany{ i ->
        i.sex.collect{ it + [id: o.id, day: i.day] }
    }
}
// [[impressions_rate:0.349, value:f, id:27858, day:2021-01-21], 
//  [impressions_rate:0.621, value:m, clicks_rate:0.22, id:27858, day:2021-01-21], 
//  [impressions_rate:0.349, value:f, id:27858, day:2021-02-25], 
//  [impressions_rate:0.651, value:m, id:27858, day:2021-02-25], 
//  [impressions_rate:0.78, value:f, clicks_rate:0.33, id:565291, day:2021-03-21], 
//  [impressions_rate:0.551, value:m, id:565291, day:2021-03-21]]
Sign up to request clarification or add additional context in comments.

3 Comments

Nice one! What if I want to iterate through age array? I mean how to bring same functionality without repeating code above and do smth based on input parameter like String field = age/sex. For age it will be i.age.collect Will be cool to loop like this: i.needed_field.collect. I tried with String param but it didnt work.
@ChokkiAST i.sex is just short for i["sex"] or i.get("sex"). This way you can parameterize it.
Good to know this things!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.