Help with list iteration: changing element value based on previous and next elements

Hello! I’m a student working on a project. I have searched for this solution everywhere. I’ve read every Python documentation regarding lists, re-read the chapters in my textbook that are supposed to cover this, searched online and watched more videos than I can count. I’m wondering if I can’t reach a solution because I’m approaching it from the entirely wrong direction.

My code:

import random
def list_eggs(chkns, eggs):
    ''' pen = [&s * eggs, 0s * randomly assigned number of indices w/out eggs],
        shuffle 0s and &s, index 0s, alter 0s to a value (0/1/2) depending 
        on number of adjacent &s '''
   
    chkns = chkns - eggs
    pen = [0] * chkns
    egg_list = ["&"] * eggs
    new_pen = pen + egg_list

    index = new_pen.index(0) 

    random.shuffle(new_pen)
    print(new_pen)

def main():
    chkns = 5
    eggs = 4
    list_eggs(chkns, eggs)

main()

Ouput:

[0, '&', '&', '&', '&']

Variables chkns and eggs are actually user inputs taken and validated in their own functions. &s represent eggs and 0s, currently, represent chickens (chkns will range from 2 to 100; eggs will range from 1 to (chkns - 1)). I need the 0s to accumulate, update, increment, or some other method such that the position remains 0 if no &s are in adjacent indices, 0 = 1 if there is one adjacent &, or 0 = 2 if both adjacent indices contain an &.

i.e. I need the above output to read:

[1, '&', '&', '&', '&'] 

I’ve tried various slices, join() attempts, insert attempts, I feel like the solution is closest to this for loop:

for index in index.new_pen[0]:
    if index[-1] == "&":
        index += 1
    elif index[1] == "&":
        index += 1

However, this raises an AttributeError:

AttributeError: 'int' object has no attribute 'new_pen'

Finding resources online and collaborating/reaching out to the programming community is encouraged. I want to do everything I can to find a solution before emailing my professor for hints/help. Thank you!

If I understand the question correctly, you first want to create lists like these:

["&", 0, "&", 0, 0, "&", 0, 0, 0, "&", "&","&", 0]
[0, 0, "&"]
["&", "&", "&", 0, 0, 0, 0, 0, 0]
[0, "&", "&", 0, 0, 0, "&", 0, "&", "&", 0]

And then you want to change the integer values in each list to either 1 or 2 depending on the number of adjacent "&", like so:

["&", 2, "&", 1, 1, "&", 1, 0, 1, "&", "&","&", 1]
[0, 1, "&"]
["&", "&", "&", 1, 0, 0, 0, 0, 0]
[1, "&", "&", 1, 0, 1, "&", 2, "&", "&", 1]

It that right?

Here’s a possible solution:

eggsnchickens = ["&", 0, "&", 0, 0, "&", 0, 0, 0, "&", "&","&", 0]
for i, e in enumerate(eggsnchickens):
    try:
        if isinstance(e, int):
            if i and eggsnchickens[i - 1] == "&":
                eggsnchickens[i] += 1 += 1
            if eggsnchickens[i+1] == "&":
                eggsnchickens[i] += 1 += 1
    except IndexError:
        pass
print(eggsnchickens)
['&', 2, '&', 1, 1, '&', 1, 0, 1, '&', '&', '&', 1]

One way is to use slice and count ‘&’:

data = [0, '&', 0, 0, 0, '&', 0, '&', '&', 0]

for i, item in enumerate(data):
    if item == 0:
        data[i] += data[max(0,i-1):i+2].count('&')

#  data -> [1, '&', 1, 0, 1, '&', 2, '&', '&', 1]

I implemented your code. I admit I was totally ignorant of the try method. I honestly thought you were telling me to try the following lines of code. And I thought by except you must’ve meant else. :man_facepalming: Even with my totally inane edits it worked 75% of runs. After realizing what I had done, I reverted to your original example and now it appears to run error-free 100% of runs. Thank you so much! I will try the next fix with a copy script to see how that one plays out. TBH, this is definitely more advanced than our current course progression, but it’s given me an opportunity to learn about so many methods and functions that I otherwide wouldn’t have. Including your provided fix!

Looks like a perfectly good solution! Thank you so much! The thing is, I had tried .count() but I didn’t, nor would I have ever thought to, include max(). If possible, I would really appreciate a plain-language explanation of what exactly is happening in the interaction between max() and .count(). I believe I nearly understand. And I’m a bit upset with myself for never having thought to use i + 1 or i - 1 as an argument. However, the way in which you’ve used it as a slice parameter, that would never have occurred to me. Thanks again!

Actually there is no interaction per se between max() and count()

Slice is suitable in this case as it doesn’t rise IndexError. Instead it returns empty list if slice is “out of bounds” totally or only items which are in bounds:

>>> l = [1, 2]
>>> l[2:4]
[]
>>> l[1:4]
[2]
>>> l[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

This is useful at the end because i+2 is out of bounds. It gives us a list without complaining/rising error that we used index which is out of bounds.

However, at the start we have another problem, at first iteration we have index 0-1 = -1. This not out of bounds, instead it is index of last item. In order to avoid this max() is used to prevent going below zero:

>>> for i in range(3):
...     print(max(0, i-1))
...
0
0
1

So the slice start i.e. first item included can’t be less than first item in list. Slice end is first item not included in slice so data[max(0,i-1):i+2].count("&") is something “give me slice of data which starts with item at previous index but not use index below zero and ends with item at next index and count how many ‘&’ are in that slice”

1 Like