HackerRank "Nested Lists" Code I completed the "Nested Lists" challenge on HackerRank, and would love any feedback on my code. 

My program is fed text with the number of students in a classroom, the name of a student, and their grade. For example: 

4
Shadab
8
Varun
8.9
Sarvesh
9.5
Harsh
10


The code should return the student with the second lowest grade. Here's the output using the above input: 

Varun


Here is my code: 

def secondlow(students):
    grades = []
    for student in students: 
        grades.append(student[1])
    sort_grades = sorted(grades)
    seclow_grade = sort_grades[0]
    for grade in sort_grades:
        if grade != seclow_grade:
            seclow_grade = grade
            break
    seclow_stud = []
    for student in students: 
        if student[1] == seclow_grade:
            seclow_stud.append(student[0])
    for name in sorted(seclow_stud): 
        print(name)



students = []
for pupil in range(int(input())):
    new_stud = [input(), float(input())]
    students.append(new_stud)
secondlow(students)
The code mostly follows usual coding style and reads good. I would just change secondlow to second_low and avoid using abreviations in variable names.

Now for the improvements:

Print vs return

Do not code too much behaviour into your functions. Let them compute their stuff and return the result so the caller can do whatever they want with it. It doesn't add much here, but it is a good habit to have.

Getting the value you are looking for


seclow_stud = []
for student in students: 
    if student[1] == seclow_grade:
        seclow_stud.append(student[0])



You do not need to store the only value you are looking for into a list. Especialy when using a for loop after that to extract the element out of the list:

for student in students: 
    if student[1] == seclow_grade:
        return student[0]


Sorted


sort_grades = sorted(grades)
seclow_grade = sort_grades[0]
for grade in sort_grades:
    if grade != seclow_grade:
        seclow_grade = grade
        break



The sorted builtin returns a list of values in increasing order. What is important to note here is that sort_grades is a list. So sort_grade[0] is the lowest grade and sort_grade[1] is the second lowest. No need to search for the value yourself, sorted already did the work for you.

seclow_grade = sorted(grades)[1]


List-comprehensions


grades = []
for student in students: 
    grades.append(student[1])



Python have these construct to easily build lists out of other iterables:

grades = [student[1] for student in students]


You can also use the same construct when building lists out of the input. Overall code would look like:

def secondlow(students):
    grades = [student[1] for student in students]
    seclow_grade = sorted(grades)[1]
    for student in students: 
        if student[1] == seclow_grade:
            return student[0]


students = [[input(), float(input())] for _ in range(int(input()))]
print(secondlow(students))


Dictionaries

I know that the challenge explicitly mentionned lists of lists, but Python have dictionaries that can map a value to another. You can use them as a replacement:

def secondlow(students):
    seclow_grade = sorted(students.values())[1]
    for student in students.items():
        if student[1] == seclow_grade:
            return student[0]


students = {}
for _ in range(int(input())):
    name = input()
    grade = float(input())
    students[name] = value
print(secondlow(students))


Or you can take advantage of the way you can retrieve items associated to others by switching the way you store your data:

def secondlow(students):
    second_low = sorted(students)[1]
    return students[second_low]


students = {}
for _ in range(int(input())):
    name = input()
    grade = float(input())
    students[value] = name  # Note the switch here
print(secondlow(students))


Edit

After reading the challenge description again, I realised that there can be multiple students with the second lowest grade. And in fact, there might be cases where there are multiple student with the lowest grade. Making my point about having the second lowest grade as sorted(grades)[1] irrelevant… Unless you remove duplicates using a set:

def second_low(students):
    second_low_grade = sorted(set(students.values()))[1]
    for student in students.items():
        if student[1] == second_low_grade:
            yield student[0]


students = {}
for _ in range(int(input())):
    name = input()
    grade = float(input())
    students[name] = value

for second_lowest_student in second_low(students):
    print(second_lowest_student)


Here I used the yield keyword to avoid building a list in second_low. What it does is allowing the function to return several results: one each time there is a match (if student[1] == second_low_grade). Here is the version using list of lists:

def second_low(students):
    grades = set(student[1] for student in students)
    seclow_grade = sorted(grades)[1]
    for student in students: 
        if student[1] == seclow_grade:
            yield student[0]


students = [[input(), float(input())] for _ in range(int(input()))]
for second_lowest_student in second_low(students):
    print(second_lowest_student)


Unfortunately, such requirements prevent you from using dictionaries the other way around.