2

I am working on the following: Write a function that returns nth lowest number of a list (or iterable in general). Return the lowest if second argument not specified Note that if a list contains duplicates, they should be handled before determining nth lowest

I got the following to work fine:

numbers = [8,9, 1,300,5, 54, 54]
def nth_lowest(b, n='N/A'):
    nums = set(b)
    if n == 'N/A':
        return min(b)
    else:
        nums = sorted(nums)
        return nums[n-1]

print(nth_lowest(numbers))
print(nth_lowest('ananasgnasgzynrmas', 6))
print(nth_lowest(numbers, 4))

But I wanted to try to write it without using those built-in SET or MIN functions. Here is what I have:

numbers = [8,9, 1,300,5, 54, 54]
def nth_lowest(b, n='N/A'):
    nums = []
    new_list = []
    for i in b:  # instead of using set()
        if i not in nums:
            nums.append(i)
    nums = sorted(nums)
    if n == 'N/A':
        while b: # instead of using min()
            minimum = b[0]
            for x in b:
                if x < minimum:
                    minimum = x
            new_list.append(minimum)
            b.remove(minimum)
        return new_list
    else:
        return nums[n-1]

print(nth_lowest(numbers))
print(nth_lowest('ananasgnasgzynrmas', 6))
print(nth_lowest(numbers, 4))

but it is error'ing out on the following:

return nums[n-1]
IndexError: list index out of range

Any ideas?

4
  • 1
    I know this isn't really what you're asking about, but there's not actually any need for a special case using min (or equivalent) when the second argument isn't passed. Just use 1 as the default n value, and the rest of the code should just work. Commented Jan 16, 2019 at 22:47
  • I am curious how that would work, if I use 1 as the default. What if the list itself didn't have a 1 in it? What if the min value of the list was 35 or something like that? Wouldn't I still need to figure out the min() of what's in the list? Commented Jan 16, 2019 at 22:53
  • But n isn't supposed to be a number in the list, it's the ordinal of the minimum you're supposed to get, isn't it? You might want a sanity check, as there's no sense in trying to find the -2'th smallest number, nor the 3000th number in a list with only 10 values. But you probably want those checks anyway for the case you're solving by sorting and indexing. No need for the default to be a special case. Commented Jan 16, 2019 at 23:01
  • I see what you are saying now, makes sense. Thanks Commented Jan 16, 2019 at 23:04

1 Answer 1

2

The problem is that you modify your input list during the algorithm: b.remove(minimum). Then, when you use numbers for the second time, it is actually empty and so is nums. You can add the following to the top of the function to create a copy of the input:

b = [x for x in b]
Sign up to request clarification or add additional context in comments.

3 Comments

thank you, that solved it. Obvious miss on my part and makes perfect sense. I will accept this answer when it lets me (5 mins or so). thanks!
Note that b[:], list(b) or b.copy() are probably more natural ways of shallow copying a list, rather than using a list comprehension.
@Blckknght You're right and in any case they are more performant.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.