nodes: 0, module; 1, function_definition; 2, function_name:sort_states; 3, parameters; 4, block; 5, identifier:states; 6, identifier:sort_list; 7, expression_statement; 8, expression_statement; 9, for_statement; 10, return_statement; 11, comment:"""
      Returns a list of sorted states, original states list remains unsorted
  
      The sort list is a list of state field: field key pairs
          For example (as YAML):
              - data: position
              - meta: created_at
      The field_key part can be a list to simplify input
          - meta: created_at
          - meta: updated_at
      or
          - meta:
              - created_at
              - updated_at
  
      
      It is also possible to sort by grouping values
          For example, with a grouping called student_id:
          - groupings:
              - student_id:
                  index: !!null     (optional: when null or missing it uses the first group in the grouping
                  values:
                      - meta: student_id
  
      A key which begins with a - inverts the sort, so
          - -data: position
      is the inverse of
          - data: position
  
      With groupings only values can have a - to invert the sort (always on the key)
          - groupings:
              - student_id:
                  index: !!null
                  values:
                      - -meta: student_id
      NOT
          - -groupings: ...
          
  
      """; 12, assignment; 13, identifier:sort_pair; 14, call; 15, block; 16, identifier:sorted_states; 17, identifier:sorted_states; 18, call; 19, identifier:reversed; 20, argument_list; 21, if_statement; 22, attribute; 23, argument_list; 24, call; 25, comparison_operator:sort_pair[0].lstrip('-') in ['data','measure','meta']; 26, block; 27, elif_clause; 28, identifier:states; 29, identifier:copy; 30, identifier:_convert_list_of_dict_to_tuple; 31, argument_list; 32, call; 33, list; 34, expression_statement; 35, comparison_operator:sort_pair[0] == 'groupings'; 36, block; 37, identifier:sort_list; 38, attribute; 39, argument_list; 40, string; 41, string; 42, string; 43, assignment; 44, subscript; 45, string; 46, for_statement; 47, subscript; 48, identifier:lstrip; 49, string; 50, string_content:data; 51, string_content:measure; 52, string_content:meta; 53, identifier:sorted_states; 54, call; 55, identifier:sort_pair; 56, integer:0; 57, string_content:groupings; 58, identifier:grouping; 59, call; 60, block; 61, identifier:sort_pair; 62, integer:0; 63, string_content:-; 64, identifier:_state_value_sort; 65, argument_list; 66, identifier:reversed; 67, argument_list; 68, expression_statement; 69, identifier:sorted_states; 70, identifier:sort_pair; 71, identifier:_state_key_function; 72, subscript; 73, assignment; 74, identifier:sort_pair; 75, integer:1; 76, identifier:sorted_states; 77, call; 78, identifier:_groupings_values_sort; 79, argument_list; 80, identifier:sorted_states; 81, identifier:grouping
code: def sort_states(states, sort_list):
      """
      Returns a list of sorted states, original states list remains unsorted
  
      The sort list is a list of state field: field key pairs
          For example (as YAML):
              - data: position
              - meta: created_at
      The field_key part can be a list to simplify input
          - meta: created_at
          - meta: updated_at
      or
          - meta:
              - created_at
              - updated_at
  
      
      It is also possible to sort by grouping values
          For example, with a grouping called student_id:
          - groupings:
              - student_id:
                  index: !!null     (optional: when null or missing it uses the first group in the grouping
                  values:
                      - meta: student_id
  
      A key which begins with a - inverts the sort, so
          - -data: position
      is the inverse of
          - data: position
  
      With groupings only values can have a - to invert the sort (always on the key)
          - groupings:
              - student_id:
                  index: !!null
                  values:
                      - -meta: student_id
      NOT
          - -groupings: ...
          
  
      """
      sorted_states= states.copy()
      for sort_pair in reversed( _convert_list_of_dict_to_tuple(sort_list) ):
          if sort_pair[0].lstrip('-') in ['data','measure','meta']:
              sorted_states= _state_value_sort(sorted_states, sort_pair, _state_key_function)
          elif sort_pair[0] == 'groupings':
              for grouping in reversed(sort_pair[1]):
                  sorted_states= _groupings_values_sort(sorted_states, grouping)
      return sorted_states