简体   繁体   中英

Sum list values within a given range using while loop

I'm trying to sum the values of a list, but only if list values are within a specific range (for example between 5 and -4 , or <= 0 )

  • Use a while loop to solve this.
  • Don't use additional lists.

The code doesn't work if I use is smaller than within the statement to check list values.

Where am I going wrong while using "<"?

My example data:

# list
ulis = [ 5 , 4 , 4 , 3 , 1 , -2 , -3 , -5 , -7 , -7 ]
 #e      0   1   2   3   4    5    6    7    8    9

# list length
ulis_l  = len (ulis)

# to count total
total_1 = 0

# index nr list
e       = 0

# list of numbers used for total 
lprt    = []

Using while with > = 0 ; this works:

print ( " # while command in progress " )
while ( ulis[e] >= 0) and ( e < ulis_l ):   
    total_1 = total_1 + ulis [e]
    lprt.append (ulis [e])
    e = e + 1
    print (total_1)

The same code, with >= 0 changed to <= 0 doesn't work, and I don't understand what is happening:

print ( " # while command in progress " )
while ( ulis[e] <= 0) and ( e < ulis_l ):
    total_1 = total_1 + ulis [e]
    lprt.append (ulis [e])
    e = e + 1
    print (total_1)

I used this code to check the output:

print ( " " )
print ( " " )
print ( " # Total sum " )
print (total_1)
print ( " " )
print ( " # values used form org list ulis for Total sum " )
print (lprt)
print ( " " )
print ( " # ulis n org values " )
print ( ulis_l )

For the first loop, this is printed:

 # while command in progress 
5
9
13
16
17


 # Total sum
17

 # values used form org list ulis for Total sum
[5, 4, 4, 3, 1]

 # ulis n org values
10

but for the second loop I see:

 # while command in progress 


 # Total sum
0

 # values used form org list ulis for Total sum
[]

 # ulis n org values
10

Don't use while , it stops at the first false result . Your loop doesn't work because the first value you test is greater than zero, and so the condition for the loop is not met:

>>> ulis[0] <= 0
False

You don't want to stop the loop when a value should not be added to the total. You want to ignore that value and continue to the next value. If you must use a while loop, then use a separate test in an if statement:

while e < ulis_l:
    if ulis[e] <= 0:
        # You can use += here instead of total_1 = total_1 + ...
        total_1 += ulis[e]
        lprt.append(ulis[e])
    e = e + 1
    print(total_1)

That's because you want to visit every value in the ulis list to test if they need to be included. Here is a demo of the above loop:

>>> ulis = [5, 4, 4, 3, 1, -2, -3, -5, -7, -7]
>>> ulis_l = len(ulis)
>>> e = total_1 = 0
>>> lprt = []
>>> while e < ulis_l:
...     if ulis[e] <= 0:
...         # You can use += here instead of total_1 = total_1 + ...
...         total_1 += ulis[e]
...         lprt.append(ulis[e])
...     e = e + 1
...     print(total_1)
...
0
0
0
0
0
-2
-5
-10
-17
-24
>>> total_1
-24
>>> lprt
[-2, -3, -5, -7, -7]

It is better, and easier, to use a for loop for this, you can loop directly over the values in a list:

for value in ulis:
    if value <= 0:
        lprt.append(value)
        total_1 += value
        print(total_1)

I moved the print() call into the test, so it only prints when a valid value i found:

>>> lprt, total_1 = [], 0
>>> for value in ulis:
...     if value <= 0:
...         lprt.append(value)
...         total_1 += value
...         print(total_1)
...
-2
-5
-10
-17
-24

If all you want to do is sum a series of values that match specific criteria, you can also put a generator expression inside a function call, using the sum() function :

total_1 = sum(value for value in ulis if value <= 0)

This is a more compact expression for the same result:

>>> sum(value for value in ulis if value <= 0)
-24

Your first example worked because the input is sorted in descending order ; the first value that's smaller than 0 (-2) can only be followed by more values that are lower than 0. If your input is large, it can be quite a smart idea to limit the number of iterations this way. You don't need a while loop for this however, you can just use the break statement :

# first loop, values greater than 0
for value in ulis:
    if value <= 0:
        # input is sorted in descending order,
        # so all remaining values _will_ be smaller.
        # To save time, we can end the loop early.
        break

    # only sums values greater than 0
    total_1 += value
    lprt.append(value)

If you want to make use of this property for your <= 0 loop, then you need to change the order in which you iterate the list. You can do this with thereversed() function here:

# second loop, summing values smaller than or equal to 0 
for value in reversed(ulis):
    if value > 0:
        # input is iterated over in ascending order,
        # so all remaining values _will_ be greater than zero.
        # To save time, we can end the loop early.
        break

    # only sums values smaller than or equal to 0
    total_1 += value
    lprt.append(value)

When you add a print() to the latter version, you can see that the values are added up in reverse order:

>>> lprt, total_1 = [], 0
>>> for value in reversed(ulis):
...     if value > 0:
...         break
...     total_1 += value
...     lprt.append(value)
...     print(total_1)
...
-7
-14
-19
-22
-24
>>> lprt
[-7, -7, -5, -3, -2]

If you need to have lprt in the correct, forward order, you can reverse the ordering again by taking the full slice with negative index:

lprt = lprt[::-1]   # reverse the lprt list

If you have two criteria, like the value is between 5 and -4 , and the input list is very large but still sorted, then you could consider using binary search to find the start and end indices of your input list, then use the range() type to generate the indices between those two points. The standard library provides the bisect module for this.

Note that this does mean the values have to be sorted in ascending order, not descending.

Take into account that range() treats the stop index as not included in the indices that are produced, so if you test for value >= -4 and value <= 5 , then you want to use bisect.bisect_right() to find the first index where the values would all be greater than 5:

import bisect

# ascending order! ulis = ulis[::-1] 

start_index = bisect.bisect_left(ulis, -4)  # values >= -4
stop_index = bisect.bisect_right(ulis, 5)  # value <= 5

total_1 = sum(ulis[index] for index in range(start_index, stop_index))

This condition is never true: ulis[e] <= 0
You used and , so both conditions ( ( ulis[e] <= 0) and ( e < ulis_l ) ) must be true. The first condition will never be true.
This problem is not related to jupyter nootbook.

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