pythonjmespath

Sorting an array/list by a specific item in an array attribute


I'm new to python/jmespath and am trying to sort the below students by their final grade in 2024. I'm sure I can come up with something in python to do it but was just wondering if there is any jmespath magic I can use. I have seen sort_by and know how to search/sort by simple attributes, but haven't yet found anything that can do this.

{
    "school": "lame",
    "students": [
        {
            "id": 1, 
            "name": "Joe", 
            "grades": [
                {"year": 2024, "final": 85}, 
                {"year": 2023, "final": 73}
            ]
        },
        {
            "id": 2, 
            "name": "Pedro", 
            "grades": [
                {"year": 2024, "final": 92}, 
                {"year": 2023, "final": 90}
            ]
        },
        {
            "id": 3, 
            "name": "Mary", 
            "grades": [
                {"year": 2024, "final": 88}, 
                {"year": 2023, "final": 70}
            ]
        }
    ]
}

Solution

  • Assuming that you want to sort by the latest year (not specifically the value 2024), you could achieve it with this:

    jmespath.search("students | sort_by(@,&grades|max_by(@,&year).final)", data)
    

    That would result in:

    [{'id': 1,
      'name': 'Joe',
      'grades': [{'year': 2024, 'final': 85}, {'year': 2023, 'final': 73}]},
     {'id': 3,
      'name': 'Mary',
      'grades': [{'year': 2024, 'final': 88}, {'year': 2023, 'final': 70}]},
     {'id': 2,
      'name': 'Pedro',
      'grades': [{'year': 2024, 'final': 92}, {'year': 2023, 'final': 90}]}]
    

    If you actually wanted to sort by the year 2024 specifically, you could use this:

    jmespath.search("students | sort_by(@,&(grades[?year==`2024`].final)[0])", data)
    

    But you would gen an error if you have any student record without any grade in 2024, as the index selector [0] would return null.

    In that case you have two options:

    1. If you want to ignore students without 2024 grade, you could use this:

      jmespath.search("students[?grades[?year==`2024`]] | sort_by(@,&(grades[?year==`2024`].final)[0])", data)
      
    2. If you want to sort them at the end or beginning:

      jmespath.search("students | sort_by(@,&(grades[?year==`2024`].final)[0]||`0`)", data)
      jmespath.search("students | sort_by(@,&(grades[?year==`2024`].final)[0]||`999`)", data)