#ISBN Verifier (Python)

13 messages · Page 1 of 1 (latest)

smoky wren
#

I've gotten a lot of tests (but not all) to pass but in trying to be more elegant, I kinda restarted. At the minute I have this code:

def is_valid(isbn):
    isbn_list = list(isbn)
    digits = ['0','1','2','3','4','5','6','7','8','9','X']
    for index, item in enumerate(isbn_list):
        if item not in digits:
            isbn_list.pop(index)
    return isbn_list

and for example on test 5 it is giving the error:

Code Run
self.assertIs(is_valid("4-598-21507-B"), False)

Test Failure
AssertionError: ['4', '5', '9', '8', '2', '1', '5', '0', '7', 'B'] is not False

I have it returning the list for the moment just so I can see what is happening and make sure the data I'll process is cleaned. I don't understand why it's successfully removing the dashes, but then allows 'B' through? It also allows through other non-X letters in other tests...

dire tendon
#

You're modifying the list while reading. After the first pop, the index that enumerate returns no longer aligns with the value returned by enumerate

#

If the input is "12", enumerate gives 0,1 then 1,2. But after you pop 1, the value 2 is no longer at index 1

#

Don't modify an object while iterating over it

smoky wren
#

I absolute understand that you are correct and I'm a beginner (plus now I vaguely remember reading that in the learning exercises 😅 ), but could you explain why multiple dashes are being successfully removed if the index is being offset each time something pops?

#

(Just so I can learn more before I rewrite it to create a new list!)

dire tendon
#

Select an example input and walk through the results line by line to see what's happening.

#

"4-598-21507-B" -- what does the list look like after each loop iteration?

#

Actually, I'm not sure if I can explain this behavior 😄

#

enumerate() might read data in chunks or something 🤷‍♂️ Modifying objects while iterating can lead to unpredictable behaviors.

smoky wren
#

||```
def is_valid(isbn):
isbn_list = []
digits = ['0','1','2','3','4','5','6','7','8','9','X','-']
for item in isbn:
if item not in digits:
return False
if item in digits[0:-1]:
isbn_list.append(item)
if len(isbn_list) != 10:
return False
for item in isbn_list[0:-2]:
if item == 'X':
return False
if isbn_list[9] == 'X':
isbn_list[9] = '10'
isbn_ints = [int(i) for i in isbn_list]
count = 10
total = 0
for i in isbn_ints:
total += i * count
count -= 1
return total % 11 == 0

#

I've finished the exercise with this code, which I think is probably messy but robust enough to deal with other problems which may not be in the tests. But I have one more question, on line 7 I had to put [0:-1] as the range because if I had it at -2, it would also remove X's. I thought -1 was supposed to be the last value in the list?

#

Okay I think I answered myself, I guess it's non inclusive as a range, because if you want to include the last item then you wouldn't have to put a finishing index at all 😅 Is that right?