2
\$\begingroup\$

I am a beginner in python. This is the Nested Lists problem from HaackerRank.

Given the names and grades for each student in a Physics class of N students, store them in a nested list and print the name(s) of any student(s) having the second lowest grade.

Note: If there are multiple students with the same grade, order their names alphabetically and print each name on a new line.

I want to improve my code with better functions available in python and I want to decrease lines of code.

if __name__ == '__main__':
    scorecard = []
    for _ in range(int(input())):
        name = input()
        score = float(input())
        scorecard.append([name, score])
    scorecard.sort(key = lambda x:x[1])
    names = []
    lowest_score = scorecard[0][1]
    second_lowest = 0
    for i in range(len(scorecard)):
        if scorecard[i][1] > lowest_score and second_lowest == 0:
            second_lowest = scorecard[i][1]

        if second_lowest != 0 and scorecard[i][1] == second_lowest:
            names.append(scorecard[i][0])

    names.sort()

    for name in names:
    print(name)
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$
  1. I'd recommend separating the logic of getting the user input from the logic of building and sorting the nested list.
  2. Your code doesn't build a nested list in a way that's useful for solving the problem; I think what you want is a list of lists of names, where each list of names corresponds to a grade.
  3. Any time you want to group things into buckets (e.g. all the names that go with a grade), it's a good time to use a dictionary!
  4. Using list comprehensions is frequently a good way to express a loop in a shorter way.
  5. Type annotations make it easier to keep track of what your code is doing (and they let you use mypy to catch bugs).

Here's my solution that uses a separate function to build the nested list, using a dictionary as an intermediate step to collect all the names for a given grade. Note that the type annotations tell you exactly where the names (strings) and grades (floats) are going at each step!

from collections import defaultdict
from typing import Dict, List, Tuple

def build_grade_lists(grades: List[Tuple[str, float]]) -> List[List[str]]:
    """
    Given a list of (name, grade) tuples, return a list of names for each grade.
    Each list is sorted, i.e.:
        the top level list is sorted from lowest to highest grade
        each list of names is sorted alphabetically
    """
    grade_lists: Dict[float, List[str]] = defaultdict(list)
    for name, grade in grades:
        grade_lists[grade].append(name)
    return [sorted(grade_lists[grade]) for grade in sorted(list(grade_lists.keys()))]

if __name__ == '__main__':
    scorecard = [(input(), float(input())) for _ in range(int(input()))]
    for name in build_grade_lists(scorecard)[1]:
        print(name)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ 2 small improvements: your function can accept any finite Iterable[Tuple[str, float]], not just lists, and [sorted(names) for _, names in sorted(grade_list.items())] expresses the final result clearer in my opinion. \$\endgroup\$ Mar 30, 2020 at 16:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.