Need help to find python code bug - python

def remove_adjacent(nums):
i = 0
while i < len(nums):
if nums[i] == nums[i+1]:
nums.remove(nums[i])
i = i + 1
else: i = i + 1
return nums
IndexError: list index out of range
Who can tell me what's wrong with my code?

There are several issues with your code.
As AbhiP points out, you're looking at pairs of consecutive items in your list - there are len(nums)-1 pairs that you should be comparing, but you're trying to compare len(nums) pairs. That's one cause of the index error.
Secondly, as John mentions, you're removing items in your list while you loop through it. If you really want to keep your current structure, you need to not increment the loop variable when you remove an item.
Correction/Clarification: This second point won't cause an index error, but it will cause bugs by making the code skip the evaluation of certain pairs, e.g. for inputs such as [1, 1, 1, 2].
Taking these two points into consideration, your code will look like:
i = 0
while i < len(nums) - 1:
if nums[i] == nums[i+1]:
nums.remove(nums[i])
else:
i += 1
Which will remove the index error.
Thirdly, nums.remove(nums[i]) will also cause non-index error bugs. Try the above code with nums being [1, 2, 3, 1, 1]. You will see that the first 1 is removed, not the 4th or 5th item in the list. This is because remove on a list gets rid of the first instance that appears in the list. You should probably do del instead, as below:
i = 0
while i < len(nums) - 1:
if nums[i] == nums[i+1]:
del nums[i]
else:
i += 1
Finally, while not a bug, best practice would suggest that you shouldn't modify a list while looping through it - this makes the code hard to reason about, and can lead to subtle bugs. Instead it's usually better if you just create a new list and return that.
new_nums = []
for i in range(len(new_nums)-1):
if nums[i] != nums[i+1]:
new_nums.append(nums[i])
new_nums.append(nums[-1])
An alternative way of writing this using zip and list comprehensions, two of Python's cool features:
new_nums = [item1 for item1, item2 in zip(nums, nums[1:]) if item1 != item2]
new_nums.append(nums[-1])

Your problem is the line while i < len(nums):. It will iterate from 0 till len-1 but the next line you are doing if nums[i] == nums[i+1] so the index will go until len.
Change it to:
while i < len(nums) - 1:

You are removing items from the list while iterating through it. The for loop has no idea that the bounds have changed, thus the error.

Related

loop through array or list in variable steps

I'm trying to figure out how to loop through an array or list in variable steps.
So for example, if I have the following list...
a = [0,0,1,0,0,1,0]
...and want to use the following logic:
Start at index 0
If index two spots away is a 0, move two spots
If index two spots away is a 1, move one spot
However, I'm not quite clear how I can implement this logic as it seems like I can't change my index value I iterate through.
Why does this snippet still return values from 0-6 instead of 0,3,6?
for idx,val in enumerate(a):
print(idx)
idx+=3
Don't use a for loop.
for loops in python are different than in C or Java. In those languages, a for loop has an initial condition, a termination condition, and an increment for each time the loop runs. Whereas in python, a for loop is more of a for each loop - you give it an iterable object, and it runs the code for every item in that iterable object.
Modifying the iterable object while you're running through it is a bad idea that can have difficult-to-predict repercussions and will usually break your code.
However, you can always use a while loop:
a = [0,0,1,0,0,1,0]
idx = 0
while(idx < len(a) - 2):
print(idx)
if a[idx + 2] == 0:
idx += 2
elif a[idx + 2] == 1:
idx += 1
print(idx)
which produces the expected output
0 1 3 4 6
Or, if you change the increments to 3 and 2 respectively, rather than 2 and 1,
0 2 5
Your reasoning is pretty confusing, and I don't see ANY application for this, but here is how I understand your problem...
The reason is because you aren't actually returning the values, you're simply returning the index + 3, which is wrong to begin with. What you're trying to do is point to a new index of the array based on its value and return the index if it contains a value greater than 0.
You need to reference the index you want, check its value, then return the index which contains a value.
a = [0, 0, 1, 0, 0, 1, 0]
for i, v in enumerate(a):
if i == 0:
print(i)
next
if v == 0:
next
else
print(i)
But let's be honest, this is extremely ugly and un-pythonic. Let's simply check for whether a[i] contains a value, and if so, return the index...
for i, v in enumerate(a):
if v or i == 0:
print(i)
The purpose of if v or i == 0 is to check if v has a value, if so, print the index. OR if we are looking at the first element of i.
If you want to EXPLICITLY move the index by two, you must set your index at the start and use a while loop, since enumerate can't help you here and doesn't actually let you move the indicies...
a = [0, 0, 1, 0, 0, 1, 0]
i = 0
while i < len(a) - 1: # -1 to avoid out of bounds error
print(i)
if a[i + 2] == 0:
i += 2
elif a[i + 2] == 1:
i += 1
print(i) # Final print statement for the last index at the end of the while loop
I want to impress upon you the fact that this solution does NOT scale with different or larger lists, which is why it isn't recommended. By simply checking for whether a value does or doesn't exist, you guarantee your accuracy.
You should simply return the index based upon whether or not it contains a value. Even for very large lists, this will be extremely fast, and will always scale, even for values greater than 1.
The only other reason I would see you would want to do this differently is if you're doing string-search algorithms.
I believe that python refreshes the the variables each iteration. So at the end of the first run, idx would be 3 but then at the start of the second run, it gets reset to 1.
If you want to go through a loop by 3s, you can still use a for loop
for i in range(0,len(a),3):
print(i)
If you want a dynamic iteration value, then you need to use a while loop.
i=0
while i<len(a):
print(i)
if(a[i]==0):
i+=2
else:
i+=1

How to update array Index in loop (IndexError: list index out of range)

I should not use advance function, as this is a logical test during interview.
Trying to remove all digits which appear more than once in array.
testcase:
a=[1,1,2,3,2,4,5,6,7]
code:
def dup(a):
i=0
arraySize = len(a)
print(arraySize)
while i < arraySize:
#print("1 = ",arraySize)
k=i+1
for k in range(k,arraySize):
if a[i] == a[k]:
a.remove(a[k])
arraySize -= 1
#print("2 = ",arraySize)
i += 1
print(a)
result should be : 1,2,3,4,5,6,7
But i keep getting index out of range. i know that it is because the array list inside the loop changed, so the "while" initial index is different with the new index.
The question is : any way to sync the new index length (array inside the loop) with the parent loop (index in "while" loop) ?
The only thing i can think of is to use function inside the loop.
any hint?
Re-Calculating Array Size Per Iteration
It looks like we have a couple issues here. The first issue is that you can't update the "stop" value in your inner loop (the range function). So first off, let's remove that and use another while loop to give us the ability to re-calculate our array size every iteration.
Re-Checking Values Shifted Into Removed List Spot
Next, after you fix that you will run into a larger issue. When you use remove it moves a value from the end of the list or shifts the entire list to the left to use the removed spot, and you are not re-checking the value that got moved into the old values removed spot. To resolve this, we need to decrement i whenever we remove an element, this makes sure we are checking the value that gets placed into the removed elements spot.
remove vs del
You should use del over remove in this case. remove iterates over the list and removes the first occurrence of the value and it looks like we already know the exact index of the value we want to remove. remove might work, but it's usage here over complicates things a bit.
Functional Code with Minimal Changeset
def dup(a):
i = 0
arraySize = len(a)
print(arraySize)
while i < arraySize:
k = i + 1
while k < arraySize: # CHANGE: use a while loop to have greater control over the array size.
if a[i] == a[k]:
print("Duplicate found at indexes %d and %d." % (i, k))
del a[i] # CHANGE: used del instead of remove.
i -= 1 # CHANGE: you need to recheck the new value that got placed into the old removed spot.
arraySize -= 1
break
k += 1
i += 1
return a
Now, I'd like to note that we have some readability and maintainability issues with the code above. Iterating through an array and manipulating the iterator in the way we are doing is a bit messy and could be prone to simple mistakes. Below are a couple ways I'd implement this problem in a more readable and maintainable manner.
Simple Readable Alternative
def remove_duplicates(old_numbers):
""" Simple/naive implementation to remove duplicate numbers from a list of numbers. """
new_numbers = []
for old_number in old_numbers:
is_duplicate = False
for new_number in new_numbers:
if old_number == new_number:
is_duplicate = True
if is_duplicate == False:
new_numbers.append(old_number)
return new_numbers
Optimized Low Level Alternative
def remove_duplicates(numbers):
""" Removes all duplicates in the list of numbers in place. """
for i in range(len(numbers) - 1, -1, -1):
for k in range(i, -1, -1):
if i != k and numbers[i] == numbers[k]:
print("Duplicate found. Removing number at index: %d" % i)
del numbers[i]
break
return numbers
You could copy contents in another list and remove duplicates from that and return the list. For example:
duplicate = a.copy()
f = 0
for j in range(len(a)):
for i in range(len(duplicate)):
if i < len(duplicate):
if a[j] == duplicate[i]:
f = f+1
if f > 1:
f = 0
duplicate.remove(duplicate[i])
f=0
print(duplicate)

Why is this Python code for finding the mode of function not working?

lst = [3, 4, 3, 5, 3]
def lstmaker(lst):
lst1 = []
x = 0
while x < len(lst):
if lst[x] == lst[x+1]:
lst1.append(lst[x])
else:
pass
x+=1
print lst1
lstmaker(lst)
I tried to make this simple program to find the the mode of list, but it just throws the index of out range error.
Thanks
For the last value of x, lst[x+1] x+1 is out of range. The while loop should be while x < len(lst) -1.
As a side note, to calculate the mode, you can simply do: max(set(lst), key=lst.count) as shown here.
The logic is incorrect.
For starters, the reason you're getting an index out of range issue is because of the line
if lst[x] == lst[x+1]
x is correctly incremented throughout your loop, but when x is at the last index, that +1 bit accesses an index that isn't in the list (e.g. index 5 of a list of size 5).
Additionally, what you were actually doing within the loop doesn't appear to be getting you towards a value for the mode. The mode is the element(s) in a list that occurs the most. One approach to tackling this problem could be using a dictionary (dict()) where the "keys" are the elements in your list, and the "values" are the amount of times each element occurs.
Try something like this:
# lst defined up here
occurrences = dict()
for x in lst:
if x in occurrences:
occurrences[x] += 1
else:
occurrences[x] = 1
mode = occurrences.keys()[0]
for k in occurrences:
if occurrences[k] >= mode:
mode = k
print(mode) # or return, etc.
This is perhaps not the most "Pythonic" solution, though it is an intuitive break-down of the logic involved in finding the mode, at least as if you were to do it by hand on paper.

Looping through a list not in order in Python

I am very new to programming, so please bear with me...I have been learning Python and I just did an assessment that involved looping through a list using your current value as the next index value to go to while looping. This is roughly what the question was:
You have a zero-indexed array length N of positive and negative integers. Write a function that loops through the list, creates a new list, and returns the length of the new list. While looping through the list, you use your current value as the next index value to go to. It stops looping when A[i] = -1
For example:
A[0] = 1
A[1] = 4
A[2] = -1
A[3] = 3
A[4] = 2
This would create:
newlist = [1, 4, 2, -1]
len(newlist) = 4
It was timed and I was not able to finish, but this is what I came up with. Any criticism is appreciated. Like I said I am new and trying to learn. In the meantime, I will keep looking. Thanks in advance!
def sol(A):
i = 0
newlist = []
for A[i] in range(len(A)):
e = A[i]
newlist.append(e)
i == e
if A[i] == -1:
return len(newlist)
This might be the easiest way to do it if your looking for the least lines of code to write.
A = [1,4,-1,3,2]
B = []
n = 0
while A[n] != -1:
B.append(A[n])
n = A[n]
B.append(-1)
print(len(B))
First of all, note that for A[i] in range(len(A)) is a pattern you certainly want to avoid, as it is an obscure construct that will modify the list A by storing increasing integers into A[i]. To loop over elements of A, use for val in A. To loop over indices into A, use for ind in xrange(len(A)).
The for loop, normally the preferred Python looping construct, is not the right tool for this problem because the problem requires iterating over the sequence in an unpredictable order mandated by the contents of the sequence. For this, you need to use the more general while loop and manage the list index yourself. Here is an example:
def extract(l):
newlist = []
ind = 0
while l[ind] != -1:
newlist.append(l[ind])
ind = l[ind]
newlist.append(-1) # the problem requires the trailing -1
print newlist # for debugging
return len(newlist)
>>> extract([1, 4, -1, 3, 2])
[1, 4, 2, -1]
4
Note that collecting the values into the new list doesn't really make sense in any kind of real-world scenario because the list is not visible outside the function in any way. A more sensible implementation would simply increment a counter in each loop pass and return the value of the counter. But since the problem explicitly requests maintaining the list, code like the above will have to do.
It's simpler to just use a while loop:
data = [1,4,-1,3,2]
ls = []
i = 0
steps = 0
while data[i] != -1:
ls.append(data[i])
i = data[i]
steps += 1
assert steps < len(data), "Infinite loop detected"
ls.append(-1)
print ls, len(ls)

Python - Sorting lists of numbers

Just learning Python and got on to the subject of sorting lists. Two types of algorithms were shown: insertion and selection. So, I had an idea and created this:
def DiffSort(lst):
lstDiff = [None] * len(lst)
i = 0
while i < len(lst):
lstDiff[i] = lst[i] - lst[i-1] if i != 0 else lst[0]
if lstDiff[i] < 0:
sbj, tmp = lst[i], lstDiff[i]
while tmp < 0:
i -= 1
tmp += lstDiff[i]
lst[i+1] = lst[i]
lst[i] = sbj
else:
i += 1
lst = [13,25,18,122,32,1,0.78,25,85,1,32,56,0.55,0.6,17]
print(lst)
DiffSort(lst)
print(lst)
Any good? Is there a similar method out there already?
list.sort() if you want to sort a list in-place.
sorted(list) if you want to return a sorted copy of the list.
The second option works with any iterable type, whereas the first is list-exclusive (although some other types may have the same or a similar function defined as well, but you can generally not expect that).
Since you seem to care about the algorithmic part of it, this may be of interest to you:
http://svn.python.org/projects/python/trunk/Objects/listsort.txt
Isn't lst.sort() good enough? It's bound to be much faster than a Python solution that has to run in O(n^2) time.

Categories

Resources