Search code examples
jsonjsonata

How do I group a group result with a list in JSONata?


I am looking for a flexible way to define JSON transformations and started trying out JSONata. For my simple usecase I need to first group elements in a list by a string value (which I can do) and then group the result with another list.

My question is is this possible and if so how? I feel that some of the higher-order functions might help but I haven't worked out how to access an object in the "outer-scope" from the inner one.

This is my obfuscated data:

{
"grades": [
    {
        "student_name": "John Smith",
        "grade": "B"
    },
    {
        "student_name": "Jane Doe",
        "grade": "A"
    }
],
"grades_by_subject": [
    {
        "student_name": "John Smith",
        "subject": "English",
        "grade": "A"
    },
    {
        "student_name": "Jane Doe",
        "subject": "Math",
        "grade": "A+"
    },
    {
        "student_name": "John Smith",
        "subject": "Math",
        "grade": "A"
    },
    {
        "student_name": "Jane Doe",
        "subject": "English",
        "grade": "C-"
    }
]

}

This is the JSONata transformation I have so far:

{
"students": grades_by_subject{`student_name`: {
        "grades_by_subject":
    $.{
            "subject":
    subject,
            "grade": grade
        }
    }
}

}

Which gives this result:

{
"students": {
    "John Smith": {
        "grades_by_subject": [
            {
                "subject": "English",
                "grade": "A"
            },
            {
                "subject": "Math",
                "grade": "A"
            }
        ]
    },
    "Jane Doe": {
        "grades_by_subject": [
            {
                "subject": "Math",
                "grade": "A+"
            },
            {
                "subject": "English",
                "grade": "C-"
            }
        ]
    }
}

}

But the result I actually want is:

{
"students": {
    "John Smith": {
        "overall_grade": {
            "student_name": "John Smith",
            "grade": "B"
        },
        "grades_by_subject": [
            {
                "subject": "English",
                "grade": "A"
            },
            {
                "subject": "Math",
                "grade": "A"
            }
        ]
    },
    "Jane Doe": {
        "overall_grade": {
            "student_name": "Jane Doe",
            "grade": "A"
        },
        "grades_by_subject": [
            {
                "subject": "Math",
                "grade": "A+"
            },
            {
                "subject": "English",
                "grade": "C-"
            }
        ]
    }
}

Solution

  • Here's one way to do it:

    {
      "students": grades_by_subject{
        student_name: {
          "overall_grade": (
            $name := student_name;
            $$.grades[student_name = $name[0]]
          ),
          "grades_by_subject": $.{
            "subject": subject,
            "grade": grade
          }
        }
      }
    }
    

    What I did is I took the student_name from the grouping context and then used it to filter the grades array from the root context.

    Check it out on the Stedi playground: https://stedi.link/CEzbQu4