0

I have a list of Objects of class Student

class Student:
    def __init__(self,id,name,cgpa):
        self.id=id
        self.name=name
        self.cgpa=cgpa
    def __str__(self):
        return "Id:{} Name:{} CGPA:{}".format(self.id,self.name,self.cgpa)

student_objects = [ Student(33,'Rumpa',3.68), Student(85,'Ashis',3.85), Student(56,'Samiha' 3.75), Student(19,'Samara',3.75), Student(22,'Fahim',3.76),]

Now I have to sort these student objects first with cgpa in DESCENDING order. If both have the same cgpa then order them using the name in ASCENDING order and if both have the same name, order them by using their ID which is unique for every student.

Generally for sorting i use sorted(Iterable,key='',reverse=''). But how can I add the CONDITION where I can go to the next step where if both have the same cgpa I can sort them by using their name.

Dhaval Taunk
  • 1,662
  • 1
  • 9
  • 17
Rohit.A
  • 1
  • 1

4 Answers4

0

You can do this:

>>> list(map(str, sorted(student_objects, key=lambda e: (-e.cgpa, e.name))))
['Id:85 Name:Ashis CGPA:3.85', 'Id:22 Name:Fahim CGPA:3.76', 'Id:19 Name:Samara CGPA:3.75', 'Id:56 Name:Samiha CGPA:3.75', 'Id:33 Name:Rumpa CGPA:3.68']

Parsing that out:

sorted(student_objects, key=lambda e: (-e.cgpa, e.name))
    # key function        ^    ^
    # Create a tuple                   ^               ^ 
    # Negative cgpa for descending.    ^
    # then name                                     ^
dawg
  • 98,345
  • 23
  • 131
  • 206
0

You may implement __lt__ function in your class.

def __lt__(self, other):
    if self.cgpa==other.cgpa:
        return self.name<other.name

    return self.cgpa<other.cgpa
taha
  • 722
  • 7
  • 15
  • There may be many ways to sort students; only one can take advantage of a predefined `__lt__` operator, and the others will still have to make use of the `key` argument to `sorted`. – chepner May 20 '20 at 14:23
0

You can use key=lambda x: (-x.cgpa, x.name), but this trick only works because there is an easy way to map int values to values that reverse the order of the sort.

A cleaner solution is to define a comparison function, then use functools.cmp_to_key to create an appropriate key function for use with sorted.

from functools import cmp_to_key

def ordering(s1, s2):
    if s1.cgpa > s2.cgpa:
        return -1  # s1 comes first
    elif s1.cgpa < s2.cgpa:
        return 1  # s2 comes first
    else:  # They're equal, fall back to name
        if s1.name < s2.name:
             return -1
        elif s1.name > s2.name:
             return 1
        else:
             return 0

student_objects = [ Student(33,'Rumpa',3.68), Student(85,'Ashis',3.85), Student(56,'Samiha' 3.75), Student(19,'Samara',3.75), Student(22,'Fahim',3.76),]

sorted_students = sorted(student_objects, key=cmp_to_key(ordering))

ordering can be defined more succintly:

# Python 2 had this built in.
def cmp(x, y):
    return -1 if x < y else 1 if x > y else 0


# Note the order of the arguments in the two calls to cmp
def ordering(s1, s2):
    return cmp(s2.cgpa, s1.cgpa) or cmp(s1.name, s2.name)
chepner
  • 497,756
  • 71
  • 530
  • 681
-1

I think this is how you want to do it

class Student:
        def __init__(self,id,name,cgpa):
            self.id=id
            self.name=name
            self.cgpa=cgpa
        def __str__(self):
            return "Id:{} Name:{} CGPA:{}".format(self.id,self.name,self.cgpa)

student_objects = [ Student(33,'Rumpa',3.68), Student(85,'Ashis',3.85), Student(56,'Samiha', 3.75), Student(19,'Samara',3.75), Student(22,'Fahim',3.76),]

student_objects.sort(key=lambda x: x.cgpa, reverse=False)

for s in student_objects:
    print(s.cgpa)
Adil Shirinov
  • 135
  • 1
  • 9