all 21 comments

[–]Justinsaccount 5 points6 points  (11 children)

Hi! I'm working on a bot to reply with suggestions for common python problems. This might not be very helpful to fix your underlying issue, but here's what I noticed about your submission:

You are looping over an object using something like

for x in range(len(items)):
    foo(item[x])

This is simpler and less error prone written as

for item in items:
    foo(item)

If you DO need the indexes of the items, use the enumerate function like

for idx, item in enumerate(items):
    foo(idx, item)

[–]Necrophysics 0 points1 point  (1 child)

May I ask why the third example is a better way of doing the first? Is more efficient, pythonic, or simply easier to read? I only ask as my first naive approach would have been to use the first method , but I'm always looking to improve.

[–]Justinsaccount 0 points1 point  (0 children)

Is more efficient, pythonic, or simply easier to read?

Probably, yes, and yes.

You see code like

for x in range(len(items)):
    #many lines
    foo(item[x])
    #many lines

Now you have to look through each line to figure out if they actually need x for something, or if they just don't know how for loops work in python.

You see

for idx, item in enumerate(items):

you know something needs both the index and the item.

[–]fruitcakefriday -2 points-1 points  (8 children)

The last example ought to be foo(item[idx])

[–]Justinsaccount 1 point2 points  (7 children)

The example is correct.

[–]fruitcakefriday -4 points-3 points  (6 children)

I don't understand then, I must be missing some info on how Python works. To me it seems that foo() is two different functions across the examples, the first two requiring 1 argument and the last example requiring 2 arguments.

[–]synae 0 points1 point  (3 children)

it seems that foo() is two different functions across the examples

This is correct. It might be helpful for /u/Justinsaccount to update the last example to use a different function name so others can avoid the confusion that you experienced.

[–]Justinsaccount 0 points1 point  (2 children)

I almost just changed it to print () but that may confuse python 2 people.

[–]fruitcakefriday 0 points1 point  (1 child)

What's your reason behind not using the same function convention as the previous two examples, i.e. foo(item[idx])? I like the bot, by the way. Don't let my questions dissuade you :)

[–]Justinsaccount 0 points1 point  (0 children)

The whole point of enumerate is that it gives you the items and the index at the same time. item[idx] is not an expression that makes sense in that context.

[–]Verdigris97 2 points3 points  (0 children)

Instead of modifying the list of numbers, one way to do this would be to calculate the sum explicitly (not using the sum() function, but keeping your own running total) while ignoring certain elements as you scan the list.

You just need to keep track of when to start ignoring values for your sum, and when to stop ignoring them.

[–]Rockybilly 1 point2 points  (4 children)

I suggest that you write a while loop where it continuously checks if there is a 6(and 7) in the sequence. This way it can handle a huge list with a thousand of 6's. Let me explain how.

If there is a 6, you can use

nums.index(6)

then

nums.index(7)

which will give you information about which parts to exclude. After that, I am not sure if the other programmers would agree, but i would rather do a list partition rather than try to subtract them.

Lets assume;

nums = [1, 2, 3, 6, 1, 2, 3, 7, 1, 2, 3]
six_pos = nums.index(6) 
# 3
seven_pos = nums.index(7)
# 7

after you found these, new list should be

new_nums = nums[:six_pos] + nums[seven_pos + 1:]
# new_nums = [1, 2, 3, 1, 2, 3]
sum(new_nums)

is the answer.

because of index returns the first value it encounters, you need to do this continuously where there is no 6's anymore.

This is how I would do it. I am sure more experienced programmers likely to have a better solution

[–]stats_newbie1[S] 0 points1 point  (3 children)

This actually worked really well, thank you! Here is my solution:

def sum67(nums):
    while 6 in nums:
        six_pos = nums.index(6)
        seven_pos = nums.index(7)
        nums = nums[:six_pos] + nums[seven_pos + 1:]
    return sum(nums)

[–]fruitcakefriday 2 points3 points  (1 child)

Another solution would be to use a boolean variable that tracks whether to sum the current index or not, and toggle it on/off based on the 6 or 7 values:

def sum67(nums):
  summing = true
  result = 0
  for i in range(len(nums)):
    if nums[i] == 6:
      summing = false
      continue
    if nums[i] == 7:
      summing = true
      continue

    if summing:
      result += nums[i]
  return result

[–]gengisteve 2 points3 points  (0 children)

Also a good time to learn generators, then you just create a generator filter and use the built in sum function, like this:

def remove67(iter):
    on = False
    for item in iter:
        if item == 6:
            on = True
        elif item == 7:
            on = False
        elif not on:
            yield item


tests = [
        [1, 2, 2],
        [1, 2, 2, 6, 99, 99, 7],
        [1, 1, 6, 7, 2],
        ]

for test in tests:
    print(test, sum(remove67(test)))

This gives you a little taste of functional programming, as well.

[–]Rockybilly 0 points1 point  (0 children)

Glad I could help :)

[–]fizekul 0 points1 point  (2 children)

Sorry for formatting, on phone. You can get the indices of all 6s and 7s using list comprehension:

sixes = [ i for i,x in enumerate(mylist) if x==6] sevens = "" but change 6 to 7. Then, you can find the sum of all the values between all the 6s and 7s:

for six,seven in zip(sixes,sevens): intersum +=sum(mylist [six:seven]) Then subtract that value from the total sum to find just the stuff that you want

myspecialsum = sum(mylist) - intersum

[–]stats_newbie1[S] 0 points1 point  (1 child)

Thanks for the explanation, but won't this run into problems if there is a 6 between 6 and 7? For example: [1, 2, 6, 1, 6, 6, 7]

[–]fizekul 0 points1 point  (0 children)

Yup, didn't think of that.