for row in c:
for i in range(len(row)):
if i not in keep:
del row[i]
i am getting this error on the last line:
IndexError: list assignment index out of range
i dont understand how it can be out of range if it exists! please help
If row is a list, then don't forget that deleting an element of a list will move all following elements back one place to fill the gap, so all of the later indices into the list will be off-by-one (and each time you delete another element, the error in each index grows by one). There are a few ways to avoid this problem -- to give one, try iterating through the list backwards.
To respond to how to iterate backwards, you could try using the extra parameters you can pass to range. The first two parameters give the range to iterate over (the lower bound being inclusive, and the upper bound exclusive), and the third parameter is the step:
>>> range(5)
[0, 1, 2, 3, 4]
>>> range(0, 5)
[0, 1, 2, 3, 4]
>>> range(3, 5)
[3, 4]
>>> range(3, 5, -1)
[]
>>> range(5, 3, -1)
[5, 4]
So, in your case, it seems you'd want:
range(len(row) - 1, -1, -1)
Or the easier to read (thanks to viraptor):
reversed(range(len(row))
Alternatively, you could try using list comprehensions (I'm assuming c is a list):
for row_number, row in enumerate(c):
c[row_number] = [x for i, x in enumerate(row) if i in keep]
Maybe you can write it like this
for row in c:
row[:] = [x for i,x in enumerate(row) if i in keep]
You should not change a list while you are iterating over it to prevent such errors.
The index existed at the start of the loop, but once you have deleted an element, the list is shorter, and does not contain the same number of elements. Thus, your indices may be wrong. If you delete from the end of the list forward, that won't invalidate list indices.
But I'm surprised about this pattern; perhaps there is a better way to solve what you're looking for? Maybe set subtraction or something similar would do what you want to do without modifying a list dozens or hundreds of times.
A more pythonic solution would be to use filter():
>>> keep = {1,3,5}
>>> row = [1, 2, 3, 4]
>>> list(filter(keep.__contains__, row))
[1, 3]
As a filtering function we can use keep.__contains__ which corresponds to the in operator of the set. Because of this we only get items from the row which are in keep.
Note: Use row[:] = filter(keep.__contains__, row) for in-place update of row or a list comprehension: c = [filter(keep.__contains__, row) for row in c]
Related
so i basically have a nested list that will be constantly changing but for example it will look like this:
[9,1,[3,5][3]]
and then I'm also getting a generated index that's formatted like this:
[2,1]
so, at index [2,1] the value is '5' but how do I write code that will automatically extract this value for me? the list will be constantly changing and the indexes will also be changing (will always be a valid index) so I cant just used a nested for loop. is there any easy way to do this?
You could iterate through the required index, and use <list>.pop(<index>) to extract the element using its index.
L = [9,1,[3,5],[3]]
idx = [2,1]
for i in idx:
L = L.pop(i)
print(L)
Output
5
If your index isn't changing in dimension, maybe you could just use the hard-coded value?
If they are changing, you can iterate with your indexes and pop the items, until you reach your item index.
lst = [9, 1, [3, 5], [3]]
idx = [2, 1]
print(lst[idx[0]][idx[1]])
// or
for i in idx:
lst = lst.pop(i)
Make a copy of L to c and loop over indices and In each loop you will get one level deeper until you reach the final value.
L = [9,1,[3,5],[3]]
indices = [2,1]
c = L.copy()
for i in indices:
c = c[i]
print(c) #5
Program description:
Program accepts a list l containing other lists. Output l where lists with length greater than 3 will be changed accordingly: the element with index 3 is going to be a sum of removed elements (from third to the end).
My solution:
l = [[1,2], [3,4,4,3,1], [4,1,4,5]]
s = 0
for i in range(len(l)-1):
if len(l[i]) > 3:
for j in range(3,len(l[i])-1):
s += l[i][j]
l[i].remove(l[i][j])
l[i].insert(len(l[i]),s)
l
Test:
Input: [[1,2], [3,4,4,3,1], [4,1,4,5]]
Expected Output: [[1, 2], [3, 4, 8], [4, 1, 9]]
Program run:
Input: [[1,2], [3,4,4,3,1], [4,1,4,5]]
Output: [[1, 2], [4, 4, 3, 1, 3], [4, 1, 4, 5]]
Question: I don't understand what can be the source of the problem in this case, why should it add some additional numbers to the end, instead of summ. I will appreciate any help.
remove is the wrong function. You should use del instead. Read the documentation to understand why.
And another bug you have is that you do not reset s. It should be set to 0 in the outer for loop.
But you're making it too complicated. I think it's better to show how you can do it really easy.
for e in l: # No need for range. Just iterate over each element
if len(e) > 3:
e[2]=sum(e[2:]) # Sum all the elements
del(e[3:]) # And remove
Or if you want it as a list comprehension that creates a new list and does not alter the old:
[e[0:2] + [sum(e[2:])] if len(e)>3 else e for e in l]
First of all, remove() is the wrong method, as it deletes by value, not index:
Python list method remove() searches for the given element in the list
and removes the first matching element.
You'd want to use del or pop().
Second of all, you're not slicing all of the elements from the end of the list, but only one value.
remove is reason why your code is not working. (as mentioned by Mat-KH in the other answer)
You can use list comprehension and lambda function to make it a two liner.
func = lambda x: x if len(x) < 3 else x[:2] + [sum(x[2:])]
l = [func(x) for x in l]
I am curious. How can I correctly iterate through a list, compare two values and delete the duplicate if it exists.
Here I created a nested for loop:
my_list = [ 1, 2, 3, 4, 5 ]
temp = [1, 5, 6]
def remove_items_from_list(ordered_list, temp):
# Removes all values, found in items_to_remove list, from my_list
for j in range(0, len(temp)):
for i in range(0, len(ordered_list)):
if ordered_list[i] == temp[j]:
ordered_list.remove(ordered_list[i])
But when I execute my my code I get an error:
File "./lab3f.py", line 15, in remove_items_from_list
if ordered_list[i] == items_to_remove[j]:
can anyone explain why?
This question, wanted to me compare two lists with one another, and these lists have two different lengths. If an item in list a matched a value in list b, we wanted then to delete it from list a.
You actually can remove items from a list while iterating over it but do read links by #ReblochonMasque.
Here is one way of removing duplicates:
def remove_items_from_list(ordered_list, temp):
n = len(ordered_list)
for i in range(n - 1, -1, -1):
if ordered_list[i] in temp:
del ordered_list[i]
Then
>>> remove_items_from_list(my_list, temp)
>>> print(my_list)
[2, 3, 4]
However, one of the easiest ways of solving your problem is to use sets:
list(set(my_list) - set(temp))
When using this approach, order of items in the resulting list may be arbitrary. Also, this will create a new list instead of modifying an existing list object. If order is important - use list comprehension:
[v for v in my_list if v not in temp]
While you iterating your loop, you remove item from orderer_list which cause index error
Try this:
def remove_items_from_list(ordered_list, temp):
list_ = [x for x in orderer_list if x not in temp]
return list_
first find the duplicated elements and then remove them from the original list.
dup_list = [item for item in temp if item in my_list]
for ele in dup_list:
my_list.remove(ele)
remove() source
You can't remove an items from the list you are iterating over. You can create a copy of the array and remove items from it.
I'm looking to break down the reverse() function and write it out in code for practice. I eventually figured out how to do it (step thru the original list backwards and append to the new 'reversed' list) but wondering why this doesn't work.
def reverse(list):
newlist = []
index = 0
while index < len(list):
newlist[index] = list[(len(list)) - 1 - index]
index = index + 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
In Python, you cannot access/update an element of a list, if the index is not in the range of 0 and length of the list - 1.
In your case, you are trying to assign to element at 0, but the list is empty. So, it doesn't have index 0. That is why it fails with the error,
IndexError: list assignment index out of range
Instead, you can use append function, like this
newlist.append(list[(len(list)) - 1 - index])
Apart from that, you can use range function to count backwards like this
for index in range(len(list) - 1, -1, -1):
newlist.append(list[index])
you don't even have to increment the index yourself, for loop takes care of it.
As suggested by #abarnert, you can actually iterate the list and add the elements at the beginning every time, like this
>>> def reverse(mylist):
... result = []
... for item in mylist:
... result.insert(0, item)
... return result
...
>>> reverse([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
If you want to create a new reversed list, you may not have to write a function on your own, instead you can use the slicing notation to create a new reversed list, like this
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
but this doesn't change the original object.
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
>>> mylist
[1, 2, 3, 4, 5]
if you want to change the original object, just assign the slice back to the slice of the original object, like this
>>> mylist
[1, 2, 3, 4, 5]
>>> mylist[:] = mylist[::-1]
>>> mylist
[5, 4, 3, 2, 1]
Note: reversed actually returns a reverse iterator object, not a list. So, it doesn't build the entire list reversed. Instead it returns elements one by one when iterated with next protocol.
>>> reversed([1, 2, 3, 4, 5])
<list_reverseiterator object at 0x7fdc118ba978>
>>> for item in reversed([1, 2, 3, 4, 5]):
... print(item)
...
...
5
4
3
2
1
So, you might want to make it a generator function, like this
>>> def reverse(mylist):
... for index in range(len(mylist) - 1, -1, -1):
... yield mylist[index]
...
...
>>> reverse([1, 2, 3, 4, 5])
<generator object reverse at 0x7fdc118f99d8>
So the reverse function returns a generator object. If you want a list, then you can create one with list function, like this
>>> list(reverse([1, 2, 3, 4, 5]))
[5, 4, 3, 2, 1]
if you are just going to process it one by one, then iterate it with a for loop, like this
>>> for i in reverse([1, 2, 3, 4, 5]):
... print(i)
...
...
5
4
3
2
1
First off don't override build-ins (list in your case) second newlist has a len of 0 therefore cannot be accessed by index.
def reverse(mylist):
newlist = [0] * len(mylist)
index = 0
while index < len(mylist):
newlist[index] = mylist[(len(mylist)) - 1 - index]
index = index + 1
return newlist
mylist = [1, 2, 3, 4, 5]
print(reverse(mylist))
you can create a list with values of the same lenght as your input list like so
newlist = [0] * len(mylist)
You need to use list.append. newlist[0] is a valid operation, if the list has atleast one element in it, but newlist is empty in this very first iteration. Also, list is not a good name for a variable, as there is a python builtin container with the same name:
def reverse(lst):
newlist = []
index = 0
while index < len(lst):
newlist.append(lst[(len(list)) - 1 - index])
index += 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
You can't assign to an arbitrary index for a 0-length list. Doing so raises an IndexError. Since you're assigning the elements in order, you can just do an append instead of an assignment to an index:
newlist.append(l[(len(l)) - 1 - index])
Append modifies the list and increases its length automatically.
Another way to get your original code to work would be to change the initialization of newlist so that it has sufficient length to support your index operations:
newlist = [None for _ in range(len(l))]
I would also like to note that it's not a good idea to name things after built-in types and functions. Doing so shadows the functionality of the built-ins.
To write the function you're trying to write, see thefourtheye's answer.
But that isn't how reverse works, or what it does. Instead of creating a new list, it modifies the existing list in-place.
If you think about it, that's pretty easy: just go through half the indices, for each index N, swap the Nth from the left and the Nth from the right.*
So, sticking with your existing framework:
def reverse(lst):
index = 0
while index < len(lst)/2:
lst[index], lst[len(lst) - 1 - index] = lst[len(lst) - 1 - index], lst[index]
index = index + 1
As a side note, using while loops like this is almost always a bad idea. If you want to loop over a range of numbers, just use for index in range(len(lst)):. Besides reducing three lines of code to one and making it more obvious what you're doing, it removes multiple places where you could make a simple but painful-to-debug mistake.
Also, note that in most cases, in Python, it's easier to use a negative index to mean "from the right edge" than to do the math yourself, and again it will usually remove a possible place you could easily make a painful mistake. But in this particular case, it might not actually be any less error-prone…
* You do have to make sure you think through the edge cases. It doesn't matter whether for odd lists you swap the middle element with itself or not, but just make sure you don't round the wrong way and go one element too far or too short. Which is a great opportunity to learn about how to write good unit tests…
probably check this out:
def reverse(lst):
newList = []
countList = len(lst) - 1
for x in range(countList,-1,-1):
newList.append(lst[x])
return newList
def main():
lst = [9,8,7,6,5,4,2]
print(reverse(lst))
main()
I loop through a list and remove the elements that satisfy my condition. But why doesn't this work, as noted below? Thank you.
>>> a=[ i for i in range(4)]
>>> a
[0, 1, 2, 3]
>>> for e in a:
... if (e > 1) and (e < 4):
... a.remove(e)
...
>>> a
[0, 1, 3]
>>> a=[ i for i in range(4)]
>>> for e in a:
... if (e > -1) and (e < 3):
... a.remove(e)
...
>>> a
[1, 3]
You cannot change something while you're iterating it. The results are weird and counter-intuitive, and nearly never what you want. In fact, many collections explicitly disallow this (e.g. sets and dicts).
Instead, iterate over a copy (for e in a[:]: ...) or, instead of modifying an existing list, filter it to get a new list containing the items you want ([e for e in a if ...]). Note that in many cases, you don't have to iterate again to filter, just merge the filtering with the generation of the data.
Why don't you just do this initially in the list comprehension? E.g.
[i for i in range(4) if i <= 1 or i >= 4]
You can also use this to construct a new list from the existing list, e.g.
[x for x in a if x <= 1 or x >= 4]
The idea of filtering is a good one, however it misses the point which is that some lists may be very large and the number of elements to remove may be very small.
In which case the answer is to remember the list indexes of the elements to remove and then iterate through the list of indexes, sorted from largest to smallest, removing the elements.
The easiest way to visualize it is to think of the iteration working on list-offsets instead of the actual items - do something to the first item, then the second item, then the third item, until it runs out of items. If you change the number of items in the list, it changes the offsets of all the remaining items in the list:
lst = [1,2,3,4]
for item in lst:
if item==2:
lst.remove(item)
else:
print item
print lst
results in
1
4
[1,3,4]
which makes sense if you step through it like so:
[1,2,3,4]
^
first item is not 2, so print it -> 1
[1,2,3,4]
^
second item is 2, so remove it
[1,3,4]
^
third item is 4, so print it -> 4
The only real solution is do not change the number of items in the list while you are iterating over it. Copy the items you want to keep to a new list, or keep track of the values you want to remove and do the remove-by-value in a separate pass.
It is not safe to remove elements from a list while iterating though it. For that exists the filter function. It takes a function(that admits one argument) and an iterable(in this case your list). It returns a new iterable of the same type(list again here) with the elements where the function applied to that element returned True:
In your case you can use a lambda function like this:
a = filter(lambda x: x > 1 and x < 4, range(4))
or if you have the list already:
a = range(4)
a = filter(lambda x: x > 1 and x < 4, a)
remember that if you are using python3 it will return an iterator and not a list.