HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Students with second lowest grade

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withlowestgradestudentssecond

Problem

I solved the Nested List Python challenge on HackerRank.com passing all test cases. Although this problem can likely be solved with a short script with clever one-liners, my approach was instead to solve it in a clean, understandable way in functional style.

Here is a summary of the challenge, see link above for full description:


Print the name(s) of any student(s) having the second lowest grade; if there are multiple students, order their names alphabetically and print each one on a new line.


The first line contains an integer, \$N\$, the number of students.
The \$2N\$ subsequent lines describe each student over \$2\$ lines; the first line contains a student's name, and the second line contains their grade.

I haven't used nested lists much until now and I found myself having to search the internet a good bit for parts of the functions (in particular the get_lowest_grade function) and I would like to improve this code as much as possible.

```
def get_students(num_students: int) -> list:
"""Returns a list of names and grades of N students read from stdin
where each name and respective grade is separated by a new line."""
students = []
for s in range(0, num_students):
name = input()
grade = float(input())
students.append([name, grade])
return students

def get_lowest_grade(students: list) -> float:
"""Returns the lowest grade from a list of students[name, grade]."""
lowest_grade_student = min(students, key = lambda x: x[1])
return lowest_grade_student[1]

def get_lowest_grade_students(students: list) -> list:
"""Returns the students with the lowest grade
from a list of students[name, grade]."""
return [s for s in students if s[1] == get_lowest_grade(students)]

def exclude_lowest_grade_students(students: list) -> list:
"""Returns a list of students with the lowest graded students excluded
from a list of students[name, grade]."""
return [s for s in students if s[1] != ge

Solution

Your code looks nice and is well documented.

Many details can be improved though.

-
num_students = int(input()) could be moved in get_students so that your code deals with input in a single place.

-
even though the problem talks about lists, it may be more idiomatic to use a list of tuples instead of a list of list. I'd highly recommend Ned Batchelder's excellent article "Lists vs. Tuples" to know more about this.

-
0 as a first argument of range is not needed as it is the default value. So you could rewrite: for s in range(0, num_students): as for s in range(num_students):.

-
Underscore (_) is pretty conventionnal for variable with an used value so you could write: for _ in range(num_students):.

-
Adding a simple print statement in get_lowest_grade shows that the function is called more often that it needs. This is a real issue when you have many students because your algorithm with go through all student for all students. Your algorithm is said to be quadratic or O(n^2). You could retrieve the lowest grade once and for all .

Your code would look like this:

def get_students() -> list:
    """Returns a list of names and grades of N students read from stdin
    where each name and respective grade is separated by a new line."""
    return [('Harry', 37.21), ('Berry', 37.21), ('Tina', 37.2), ('Akriti', 41), ('Harsh', 39),
            ('Harry', 37.21), ('Berry', 37.21), ('Tina', 37.2), ('Akriti', 41), ('Harsh', 39),
            ('Harry', 37.21), ('Berry', 37.21), ('Tina', 37.2), ('Akriti', 41), ('Harsh', 39)]
    num_students = int(input())
    students = []
    for s in range(num_students):
        name = input()
        grade = float(input())
        students.append((name, grade))
    return students

def get_lowest_grade(students: list) -> float:
    """Returns the lowest grade from a list of students[name, grade]."""
    lowest_grade_student = min(students, key = lambda x: x[1])
    return lowest_grade_student[1]

def get_students_with_grade(students: list, grade: float) -> list:
    """Returns the students with the lowest grade 
    from a list of students(name, grade)."""
    return [s for s in students if s[1] == grade]

def get_students_without_grade(students: list, grade: float) -> list:
    """Returns a list of students with the lowest graded students excluded
    from a list of students(name, grade)."""
    return [s for s in students if s[1] != grade]

def get_student_names_sorted_alpha(students: list) -> list:
    """Returns a list of names sorted alphabetically from a list of students[name, grade]"""
    names = [s[0] for s in students]
    return sorted(names)

def main():
    students = get_students()
    lowest_grade = get_lowest_grade(students)
    students2 = get_students_without_grade(students, lowest_grade)
    lowest_grade2 = get_lowest_grade(students2)
    second_lowest = get_students_with_grade(students2, lowest_grade2)
    for name in get_student_names_sorted_alpha(second_lowest):
        print(name)

if __name__ == '__main__':
    main()


A different algorithm could be written using a different data structure like a dictionnary or a counter.

Code Snippets

def get_students() -> list:
    """Returns a list of names and grades of N students read from stdin
    where each name and respective grade is separated by a new line."""
    return [('Harry', 37.21), ('Berry', 37.21), ('Tina', 37.2), ('Akriti', 41), ('Harsh', 39),
            ('Harry', 37.21), ('Berry', 37.21), ('Tina', 37.2), ('Akriti', 41), ('Harsh', 39),
            ('Harry', 37.21), ('Berry', 37.21), ('Tina', 37.2), ('Akriti', 41), ('Harsh', 39)]
    num_students = int(input())
    students = []
    for s in range(num_students):
        name = input()
        grade = float(input())
        students.append((name, grade))
    return students

def get_lowest_grade(students: list) -> float:
    """Returns the lowest grade from a list of students[name, grade]."""
    lowest_grade_student = min(students, key = lambda x: x[1])
    return lowest_grade_student[1]

def get_students_with_grade(students: list, grade: float) -> list:
    """Returns the students with the lowest grade 
    from a list of students(name, grade)."""
    return [s for s in students if s[1] == grade]

def get_students_without_grade(students: list, grade: float) -> list:
    """Returns a list of students with the lowest graded students excluded
    from a list of students(name, grade)."""
    return [s for s in students if s[1] != grade]

def get_student_names_sorted_alpha(students: list) -> list:
    """Returns a list of names sorted alphabetically from a list of students[name, grade]"""
    names = [s[0] for s in students]
    return sorted(names)

def main():
    students = get_students()
    lowest_grade = get_lowest_grade(students)
    students2 = get_students_without_grade(students, lowest_grade)
    lowest_grade2 = get_lowest_grade(students2)
    second_lowest = get_students_with_grade(students2, lowest_grade2)
    for name in get_student_names_sorted_alpha(second_lowest):
        print(name)

if __name__ == '__main__':
    main()

Context

StackExchange Code Review Q#142512, answer score: 6

Revisions (0)

No revisions yet.