AlSub
AlSub

Reputation: 1055

Finding two smallest values in a list

I am trying to return the two smallest values for a numeric list as a tuple. However the next code keeps returning first two values of a list.

def test(): 
  list = [4, 5, 1, 9, -2, 0, 3, -5] 
  min1 = list[0]
  min2 = list[1]

  length = len(list)
  
  for i in range(1, length):
    if list[i] < list[0]:
        if list[0] < list[1]:
            list[i] = list[1]
        else:
            list[i] = list[1] 
    else:
        if list[i] < list[1]:
            list[i] = list[1]
    print(min1, min2)

    return (min1, min2) 

test()

Console output:

4,5

Is there any way to do this by iterating?

Upvotes: 0

Views: 2820

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1125398

The variables min1 and min2 don't update, they are not references to the first and second element of the list, they are references to the values that were at indexes 0 and 1 when the assignment takes place. That you later change list[0] and list[1] doesn't matter.

In Python, both list indexes and variables are just references to the actual objects. Think of Python objects like balloons, and variables and indices are just labels tied to the a string attached to the balloon. You can attach multiple labels to the balloons, but if you than move the label to a different balloon, the other labels tied to the old balloon don't follow along.

Here, min1 and min2 were tied to balloons that already had the 0 and 1 index labels tied to them. Later on, when you assign to list[i], you retied a specific index label to another balloon, but the min1 and min2 labels didn't change.

As a side note, this part of your code has a rather obvious error:

if list[0] < list[1]:
    list[i] = list[1]
else:
    list[i] = list[1] 

Both branches do the exact same thing, assign list[1] to list[i].

You are otherwise doing completely incorrect assignments even if you hoped that changing list[0] and list[1] inside the loop would make a change to the values of min1 and min2, you are changing list[i], the other value in the list, the one that's supposed to be smaller.

So for i = 2, list[i] is list[2] and list[2] < list[0] is true (1 < 4), you then test if list[0] < list[1] (also true, 4 < 5), so you do list[i] = list[1], setting list[2] = 5, leaving list[0] set to 4, list[1] set to 5, and in effect throwing away the 1 value that was present at list[2] before.

Rather than compare with list[0] or list[1], have your loop update min1 and min2:

# min1 is always smaller than min2
min1, min2 = list[:2]
if min2 < min1:
    min1, min2 = min2, min1

for i in range(1, length):
    if list[i] < min1:
        min1 = list[i]
    elif list[i] < min2:  # but equal to or greater than min1!
        min2 = list[i]

I also made sure that min1 < min2 at the start, that makes the loop a lot simpler because if list[i] < min1 is not true then it can perhaps be smaller than min2 but you don't need to test against min1 a second time.

Note that we assign the list[i] values to min1 and min2 here, you want to update those two variables with the value you just tested, provided list[i] is indeed smaller than what you had before.

Upvotes: 4

Related Questions