简体   繁体   中英

List slicing and finding second highest value python

I need to write a function that takes a list as a parameter and find the second highest value in the list. Return that value. If there is only one value in the list, return that value. If the list is empty, return 0.

To create the list, I prompt the user for numbers until the user enters a sentinel value of -1. Then I prompt the user for a beginning location (loc) and a length (len). I would extract a list slice that begins at index loc and has length len and then use my function to find the second highest value in that list slice.

Here's the code I have so far:

userList = []

def main():
    inputList = int(input("Enter a number >= 0. Enter -1 to end input: "))
    while inputList != -1:
        userList.append(inputList)
        inputList = eval(input("Enter a number >= 0. Enter -1 to end input: "))

    return extractList(userList)

def extractList(userList):
    loc = int(input("Enter a starting location: "))
    length = int(input("Enter a lenghth: "))

    selSlice = userList[loc:loc + length]

    if len(selSlice) == 1:
        return selSlice[0]
    if len(selSlice) == 0:
        return 0

    num = max(selSlice)
    occ = userList.count(num)
    for i in range(occ):
        userList[userList.index(num):userList.index(num)+1] = ''

    print("Second highest value in ", selSlice, "is ", max(selSlice))

main()

I'm testing to see if the slice works, but it seems to take the starting index of loc and goes to ending index of len instead of going out the length of len.

Example, if I have a list:

[1, 3, 7, 21, 37, 76, 23, 91, 15, 11, 2, 4]

and my loc is 3 and my len is 6, the result should be [21, 37, 76, 23, 91, 15] . However, I'm not getting this desired result, instead I would get [21, 37, 76] .

What should my extractList(a) be? Also, if you could help me with the function to find the second highest value, it'd be great. Thanks for any input!

Edit:

Ok, so I'm on the right track now, thanks to Chris Arena and To Click or Not to Click . (Code has been updated)

However, the code above gives me the second highest value of the whole list, not the sliced list. I'm not sure if all the variables are correct.

If my userList is [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] and my loc is 6 and length is 4, I get [4, 6, 8, 10] back, as I should, but the second highest value of the slice is 9, which is the second highest of userList, not the slice.

I tried to change userList to selSlice starting from line if len(userList) == 1: through to the end to see if that made a difference. It did, but the results were questionable (aka wrong). I used the same userList, loc, and length as mentioned in the previous paragraph. I got [4, 6, 8] back as the slice (wrong) and the second highest value is 8, which is wrong for the slice the program returned but right for my slice that I requested. So I'm not sure what could be wrong here. Any advice?

Edit to Edit:

My latest code shows the correct slice, but the wrong 2nd highest value. I'm getting: Second highest value in [4, 6, 8, 10] is 10 Not sure what needs fixing =\\

Try this to get the second highest:

def extract(arr, start, finish):
    if start < 0 or finish > len(arr)-1:
        return "Please enter valid start/end points"
    lst = arr[start:finish]
    if len(lst) == 1:
        return lst[0]
    if len(lst) == 0:
        return 0
    num = max(lst)
    occ = lst.count(num)
    for i in range(occ):
        lst[lst.index(num):lst.index(num)+1] = ''
    return max(lst)

This runs as:

>>> extract([6, 7, 8, 9, 1, 5, 3, 7, 2], -1, 8)
'Please enter valid start/end points'
>>> extract([6, 7, 8, 9, 1, 5, 3, 7, 2], -3, 8)
'Please enter valid start/end points'
>>> extract([6, 7, 8, 9, 1, 5, 3, 7, 2], 3, 8)
7
>>> 

Few tips:

Don't use eval . eval is evil. If you have to use an eval like function, try ast.literal_eval() , or just cast int() .

Here is your edited code:

userList = []

def main():
    inputList = int(input("Enter a number >= 0. Enter -1 to end input: "))
    while inputList != -1:
        userList.append(inputList)
        inputList = eval(input("Enter a number >= 0. Enter -1 to end input: "))

    return extractList(userList)

def extractList(userList):
    loc = int(input("Enter a starting location: "))
    length = int(input("Enter a lenghth: "))

    selSlice = userList[loc:loc + length]
    orig = list(selSlice)

    if len(selSlice) == 1:
        return selSlice[0]
    if len(selSlice) == 0:
        return 0

    num = max(selSlice)
    occ = selSlice.count(num)
    for i in range(occ):
        selSlice[selSlice.index(num):selSlice.index(num)+1] = ''

    print("Second highest value in ", orig, "is ", max(selSlice))

main()

This runs as:

bash-3.2$ python3 test.py
Enter a number >= 0. Enter -1 to end input: 2
Enter a number >= 0. Enter -1 to end input: 7
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 9
Enter a number >= 0. Enter -1 to end input: 3
Enter a number >= 0. Enter -1 to end input: 1
Enter a number >= 0. Enter -1 to end input: 6
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 2
Enter a number >= 0. Enter -1 to end input: 8
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 2
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 3
Enter a number >= 0. Enter -1 to end input: 7
Enter a number >= 0. Enter -1 to end input: -1
Enter a starting location: 2
Enter a lenghth: 12
Second highest value in  [4, 9, 3, 1, 6, 4, 2, 8, 4, 2, 4, 3] is  8
bash-3.2$ 

Another approach:

You can use sorted() and get the second to last value:

userList = []

def main():
    inputList = int(input("Enter a number >= 0. Enter -1 to end input: "))
    while inputList != -1:
        userList.append(inputList)
        inputList = eval(input("Enter a number >= 0. Enter -1 to end input: "))

    return extractList(userList)

def extractList(userList):
    loc = int(input("Enter a starting location: "))
    length = int(input("Enter a lenghth: "))

    selSlice = userList[loc:loc + length]

    if len(selSlice) == 1:
        return selSlice[0]
    if len(selSlice) == 0:
        return 0

    num = sorted(selSlice)[-2]

    print("Second highest value in ", selSlice, "is ", num)

main()

Which runs as:

bash-3.2$ python3 test.py
Enter a number >= 0. Enter -1 to end input: 2
Enter a number >= 0. Enter -1 to end input: 7
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 9
Enter a number >= 0. Enter -1 to end input: 3
Enter a number >= 0. Enter -1 to end input: 1
Enter a number >= 0. Enter -1 to end input: 6
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 2
Enter a number >= 0. Enter -1 to end input: 8
Enter a number >= 0. Enter -1 to end input: 4 
Enter a number >= 0. Enter -1 to end input: 2
Enter a number >= 0. Enter -1 to end input: 4
Enter a number >= 0. Enter -1 to end input: 3
Enter a number >= 0. Enter -1 to end input: 7
Enter a number >= 0. Enter -1 to end input: -1
Enter a starting location: 2
Enter a lenghth: 12
Second highest value in  [4, 9, 3, 1, 6, 4, 2, 8, 4, 2, 4, 3] is  8
bash-3.2$

Few things on good Python practice:

You're better off calling int() on the input, rather than eval(). Eval is unsafe for a variety of reasons, and if you call int() it will throw an exception immediately if the user gives a bad value (the string 'blah', for example). You can then catch this exception and not add the bad value to your list. If the user can enter non-integers, use float() instead.

You also don't want to override built in names, in this case 'len'. Choose a different name for that variable.

When you're doing slicing on iterables (like lists), you can leave the slice empty and it will be smart about what it takes for the new list. For example, userList[loc:], or userList[:], or userList[:3].

Finally, it seems like what you should be doing is taking the starting location and taking the next 'length' of characters. So you need to use userList[loc:loc+length] ... this also has the potential of failing if the user gives more values than your list holds, though, so you should do some error checking here too.

The slice is straightforward:

li=[1, 3, 7, 21, 37, 76, 23, 91, 15, 11, 2, 4]

loc=3
run=6

sl=li[loc:loc+run]

print(sl)
# [21, 37, 76, 23, 91, 15]

Then second highest:

print(sorted(set(sl))[-2])   # use 'set' in case there are duplicates...
# 76

Timing of various ways to find the SECOND max:

def f1(li):
    return sorted(set(li))[-2]

def f2(li):
    max_n=max(li)
    while max_n in li:
        li.remove(max_n)

    return max(li)

def f3(li):
    set_li=set(li)
    set_li.remove(max(set_li))
    return max(set_li)

def f4(li):
    max_n=max(li)
    li_=[x for x in li if x!=max_n]   
    return max(li_)

def f5(li):
    return sorted(li)[-2]    


if __name__ =='__main__':
    import timeit   
    import random
    li=[random.randint(0, 100) for _ in range(100000)]  
    for f in (f1, f2, f3, f4, f5):
        print('{}: {:.4} secs'.format(f.__name__,
                   timeit.timeit("f(li[:])", setup="from __main__ import li, f", number=10)))

Prints:

f1: 0.01876 secs
f2: 14.61 secs         # OUCH
f3: 0.01861 secs
f4: 0.09974 secs
f5: 0.2418 secs

You can see that sorting the unique values is pretty fast way to get the Nth highest (or lowest) value

Another way to remove item from list is use remove(value) method, example:

while maxValue in lst:
    lst.remove(maxValue)

Also, it's better to write a separate function to find second highest value from list. Note that lst will be modified after call this function:

def extractSecondHighest(lst):
    """
    Extraction second highest value from lst.
    """
    #lst = lst[:] <-- uncomment this line if you don't want `lst` to be modified 
    if len(lst) == 1:
        return lst[0]
    if len(lst) == 0:
        return 0

    maxValue = max(lst) # obtain max value
    while maxValue in lst: # while lst has maxValue, remove it
        lst.remove(maxValue)
    return max(lst)

Then, your extractList function will be:

def extractList(userList):
    loc = int(input("Enter a starting location: "))
    length = int(input("Enter a lenghth: "))

    selSlice = userList[loc:loc + length] # don't forget to check exception when loc + length is larger then list's length

    secondHighest = extractSecondHighest(selSlice)

    print("Second highest value in", userList, "is", secondHighest)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM