How to iterate over a python list? - python

If I execute this code, the following error message occurs:
IndexError: list index out of range python
def reverse_invert(lst):
inverse_list = []
for i in lst:
if isinstance( i, int ):
inverse_list.append(lst[i])
#print(inverse_list)
print(i)
else:
break
return inverse_list
Why is it?

for i in lst:
will iterate the elements of lst.
If you want to iterate indexes, use
for i in range(len(lst)):
If you want both the element and the index, use enumerate:
for i, el in enumerate(lst):

You are iterating the elements of list but trying to use the element as index. You should change your code like this:
def reverse_invert(lst):
inverse_list = []
for i in lst:
if isinstance( i, int ):
inverse_list.append(i) # changed this one.
#print(inverse_list)
print(i)
else:
break
return inverse_list

List comprehension would work fine:
a = [1, 'a', 2, 3]
print [d for d in a[::-1] if isinstance(d, int)]
And if you want to reverse it just tiny change would do:
a = [1, 'a', 2, 3]
print [d for d in a[::-1] if isinstance(d, int)]
Or maybe I missed your point.

Generally it means that you are providing an index for which a list element does not exist.
E.g, if your list was
[1, 3, 5, 7], and you asked for the element at index 10, you would be well out of bounds and receive an error, as only elements 0 through 3 exist.

Related

Remove duplicates without using list mutation

I am trying to remove adjacent duplicates from a list without using list mutations like del or remove. Below is the code I tried:
def remove_dups(L):
L = [x for x in range(0,len(L)) if L[x] != L[x-1]]
return L
print(remove_dups([1,2,2,3,3,3,4,5,1,1,1]))
This outputs:
[1, 3, 6, 7, 8]
Can anyone explain me how this output occurred? I want to understand the flow but I wasn't able to do it even with debugging in VS code.
Input:
[1,2,2,3,3,3,4,5,1,1,1]
Expected output:
[1,2,3,4,5,1]
I'll replace the variables to make this more readable
def remove_dups(L):
L = [x for x in range(0,len(L)) if L[x] != L[x-1]]
becomes:
def remove_dups(lst):
return [index for index in range(len(lst)) if lst[index] != lst[index-1]]
You can see, instead of looping over the items of the list it is instead looping over the indices of the array comparing the value at one index lst[index] to the value at the previous index lst[index-1] and only migrating/copying the value if they don't match
The two main issues are:
the first index it is compared to is -1 which is the last item of the list (compared to the first)
this is actually returning the indices of the non-duplicated items.
To make this work, I'd use the enumerate function which returns the item and it's index as follows:
def remove_dups(lst):
return [item for index, item in enumerate(lst[:-1]) if item != lst[index+1]] + [lst[-1]]
Here what I'm doing is looping through all of the items except for the last one [:-1] and checking if the item matches the next item, only adding it if it doesn't
Finally, because the last value isn't read we append it to the output + [lst[-1]].
This is a job for itertools.groupby:
from itertools import groupby
def remove_dups(L):
return [k for k,g in groupby(L)]
L2 = remove_dups([1,2,2,3,3,3,4,5,1,1,1])
Output: [1, 2, 3, 4, 5, 1]

How to filter multi-dimensional list in Python 3?

I have list:
[1,4,4,2,[[[4],5]],5,6]
How can i filter this list, that i will have unique values, but save dimensions for elements?
result:
[1,4,2,[[5]],6]
you can have recursive function that works like any other unique-finding solution you might find online, but when it encounters an inner list, it calls itself with this inner list (getting only the uniques from it), also passing as argument the set of already seen elements (so that the "uniques from it" is defined as entirely new elements).
try this:
lst = [1, 4, 4, 2, [[[4], 5]], 5, 6]
def unique(lst, already_seen):
result = []
for item in lst:
if isinstance(item, list):
inner_uniques = unique(item, already_seen)
if len(inner_uniques) > 0:
result.append(inner_uniques)
else: # assuming item is a number
if item in already_seen:
continue
else: # new number
result.append(item)
already_seen.add(item)
return result
result = unique(lst, set()) # starting with an empty ser
print(result)
Output:
[1, 4, 2, [[5]], 6]
Here's a version that modifies the original list instead of making a new one (and returns it, which isn't so Pythonic but can be useful):
def remDups( s, seen=set() ):
to_pop = []
for i,v in enumerate(s):
if isinstance(v,list):
remDups( v, seen )
elif v in seen:
to_pop = [i]+to_pop
else:
seen.add(v)
for i in to_pop:
s.pop(i)
return s

Print a new line in between a list in python

I have a list of potentially unknown length like so:
list = [[1,2], [3,4,5], [6]]
I have a for loop that prints these items out, but I also want to be able to add an extra new line in between.
1
2
3
4
5
6
I don't want an additional new line after the final item or before the first. I have a for loop that prints out spaces in between the items in the line. However, there are instances where 1 or more indices are empty. In that case, I don't want to add an extra new line. I've managed to figure out if the first or last index is empty and how to deal with that, but not a middle index.
For example the above result should also be obtainable with this:
list = [[1, 2], [], [3, 4, 5], [6]]
I'm not sure what's the best way to determine this.
Use enumerate() like this:
for i, sub in enumerate(mylist):
if i: print() # If you are using Python 2, remove the parentheses
for x in sub:
print(x)
Edit: I misunderstood your question a little bit. Since your second example list had invalid syntax, I assumed that meant just two sublists. The comment by PaulRooney has cleared that up, so you can do this:
should_print = False
for sub in mylist:
if should_print: print()
for x in sub:
print(x)
should_print = bool(sub)
Since you don't want extra space before or after it sounds like str.join will probably be closer to what you want then other answers that print it chunk by chunk, first you need some generator for each chunk formatted on its own:
def parse_list(mylist):
for seq in mylist:
if seq: #is not empty
yield "\n".join(map(str,seq))
#with print function
print(*parse_list(stuff), sep="\n\n")
#old print statement (but still forward compatible)
print ("\n\n".join(parse_list(stuff)))
you could also just use a generator expression if you only need to use this once:
each_block = ("\n".join(map(str,seq)) for seq in stuff if seq)
print("\n\n".join(each_block))
Or you could condense this to a single line but I wouldn't:
print("\n\n".join("\n".join(map(str,seq)) for seq in stuff if seq))
list = [[1,2],[], [3],[5,6,7],[8]]
first = True
for item in list:
if first and item:
first = False
for number in item:
print(number)
else:
if item:
print('')
for number in item:
print(number)
list = [[1, 7, 3], [], [7, 10], None, [2, 3, 4]]
for i, v in enumerate(list):
if v:
if i != 0:
print
for x in v:
print x
When you say an index is "empty" do you mean it contains None?
list1 = [[1,2], None, [3]]
Or it contains an empty list?
list1 = [[1,2], [], [3]]
Either way, I think this would work:
for i,j in enumerate(list1):
if j:
for k in j:
print k
if i+1 < len(list1):
print
Edit: The above assumes that your input is always a list of lists, as your question seems to imply.
Comparing the current index to the length of the list to determine whether we're at the end will fail if the list has one or more empty elements at the end, e.g.:
list3 = [[1,2], [], [3], []]
This will output:
(start)
1
2
3
(end)
Note that there's a line break after the 3, which it sounds like you wouldn't want. You'd need some additional logic to account for that, if it's a possibility.
This should do the trick:
for idx, i in enumerate(l):
if i:
for j in i:
print(j)
if idx < len(l) - 1:
print()

"local variable 's' referenced before assignment" error. How do I fix?

I am trying to make a list that takes in numbers and removes all the old numbers in the list and returns s the result, but it keeps on giving me the error "local variable 's' referenced before assignment" How do I fix it?
def purify(lst):
for item in lst:
if item%2 != 0:
lst.remove(item)
s.append(lst)
s = []
return s
It isn't clear why you want to return a list containing a single list. Why not just return the "purified" list?
def purify(lst):
return [item for item in lst if item % 2 == 0]
This uses a list comprehension. Demo:
>>> purify([1, 2, 3, 4, 5])
[2, 4]
For more flexibility, you could also make a version where you pass your own rule:
def purify(lst, rule):
return [item for item in lst if rule(item)]
Demo:
>>> purify([1, 2, 3, 4, 5], lambda i: i % 2)
[1, 3, 5]
You need to assign s to empty list first and then append.
def purify(lst):
for item in lst:
if item%2 != 0:
lst.remove(item)
s = []
s.append(lst)
return s
As #jonrsharpe suggested, don't remove the item while iterating over list. The good approach is using the list comprehension:
[i for i in lst if i%2!=0]
You should not remove items while iterating, because the list should not be changed when using iterators (this is what you do with "in").
def purify(lst):
s = []
for item in lst:
if item%2 == 0:
s += [item]
return s
btw: This can be done very fast with numpy.
import numpy as np
...
def purify(lst):
indices = np.where(np.mod(lst,2) == 0)
return np.array(lst)[indices]
Simplify your life with filter:
filter(lambda x: x % 2 == 0, range(5))

Indexing of Same Values in a List

Due to list.index(x) will only return the index in the list of the first item whose value is x. Is there any way to return every index of same values in the list.
For example, I have a list containing some same values like:
mylist = [(A,8), (A,3), (A,3), (A,3)]
I want to return:
index_of_A_3 = [1, 2, 3]
mylist = [(A,8), (A,3), (A,3), (A,3)]
def indices( mylist, value):
return [i for i,x in enumerate(mylist) if x==value]
print indices(mylist, (A,3))
# [1, 2, 3]
Replace (A,3) with what you want or use a lambda.
[i for i in range(len(mylist)) if mylist[i]==(A,3)]
It's kinda ugly but:
index_of_A_3 = [i for i in range(len(mylist)) if mylist[i] == (A,3)]
more quickly !!!!
index_of_A_3= np.where(mylist == (A,3))[0]

Categories