This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 1 year ago.
I want to remove all elements in a list which contains (or does not contain) a set of specific characters, however I'm running in to problems iterating over the list and removing elements as I go along. Two pretty much equal examples of this is given below. As you can see, if two elements which should be removed are directly following each other, the second one does not get removed.
Im sure there are a very easy way to do this in python, so if anyone know it, please help me out - I am currently making a copy of the entire list and iterating over one, and removing elements in the other...Not a good solution I assume
>>> l
['1', '32', '523', '336']
>>> for t in l:
... for c in t:
... if c == '2':
... l.remove(t)
... break
...
>>> l
['1', '523', '336']
>>> l = ['1','32','523','336','13525']
>>> for w in l:
... if '2' in w: l.remove(w)
...
>>> l
['1', '523', '336']
Figured it out:
>>> l = ['1','32','523','336','13525']
>>> [x for x in l if not '2' in x]
['1', '336']
Would still like to know if there is any way to set the iteration back one set when using for x in l though.
List comprehensions:
l = ['1', '32', '523', '336']
[ x for x in l if "2" not in x ]
# Returns: ['1', '336']
[ x for x in l if "2" in x ]
# Returns: ['32', '523']
l = ['1', '32', '523', '336']
stringVal = "2"
print(f"{[ x for x in l if stringVal not in x ]}")
# Returns: ['1', '336']
print(f"{[ x for x in l if stringVal in x ]}")
# Returns: ['32', '523']
If I understand you correctly,
Example:
l = ['1', '32', '523', '336']
[x for x in l if "2" not in x]
# Returns: ['1', '336']
fString Example:
l = ['1', '32', '523', '336']
stringVal = "2"
print(f"{[x for x in l if stringVal not in x]}")
# Returns: ['1', '336']
might do the job.
In addition to #Matth, if you want to combine multiple statements you can write:
l = ['1', '32', '523', '336']
[ x for x in l if "2" not in x and "3" not in x]
# Returns: ['1']
fString Example
l = ['1', '32', '523', '336']
stringValA = "2"
stringValB = "3"
print(f"{[ x for x in l if stringValA not in x and stringValB not in x ]}")
# Returns: ['1']
Problem you could have is that you are trying to modify the sequence l same time as you loop over it in for t loop.
Related
I have a list
A = [['1'],['1','2'],['1','2','3','1','2'],['3','3','3']]
and I want to make my list to
A = [['1'],['1','2'],['1','2','3'],['3']]
ie I want to remove duplicate elements within the elements in a list ..
One-liner (If order doesn't matter) :
A = [['1'],['1','2'],['1','2','3','1','2'],['3','3','3']]
A = [list(set(a)) for a in A]
print(A) # => [['1'], ['2', '1'], ['3', '2', '1'], ['3']]
One-liner (If order matters) :
A = [['1'],['1','2'],['1','2','3','1','2'],['3','3','3']]
A = [sorted(set(a), key=a.index) for a in A]
print(A) # => [['1'], ['1', '2'], ['1', '2', '3'], ['3']]
A functional version, with functools:
>>> import functools
>>> A = [['1'],['1','2'],['1','2','3','1','2'],['3','3','3']]
>>> print ([functools.reduce(lambda result,x:result if x in result else result+[x], xs, []) for xs in A])
[['1'], ['1', '2'], ['1', '2', '3'], ['3']]
The lambda function adds an element to the result list only if that element is not present in the list. Not very efficient, but keeps the order of elements.
Also note that with Python 2, you don't need to import functools: reduce is a builtin function.
You can use a generator:
def remove_dups(l):
for a in l:
new_l = []
for b in a:
if b not in new_l:
new_l.append(b)
yield new_l
A = [['1'],['1','2'],['1','2','3','1','2'],['3','3','3']]
print(list(remove_dups(A)))
Output:
[['1'], ['1', '2'], ['1', '2', '3'], ['3']]
why such construction doesn't work?
l = [1,2,3]
for x in l:
x = str(x)
print(l)
it returnes:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
instead of expected:
['1', '2', '3']
['1', '2', '3']
['1', '2', '3']
For each iteration you're printing the original list without modifying it.
Use map()
list(map(str, l))
>> ['1', '2', '3']
or a list comprehension
l = [str(x) for x in l]
When you do x = str(x) it changes the value in x to str (and not the element in your list l)
But as you are trying to change the list l
I suggest you try a list comprehension:
l = [str(x) for x in l]
You need to store back the casted x back to the list as below:
l = [1,2,3]
new_l = []
for x in l:
new_l.append(str(x))
print(new_l)
Also,if you're not accustomed with map (see other answers) you could use :
for i,x in enumerate(l):
l[i] = str(x)
But other answers are just better.
I have the following list_A:
['0', '1', '2', '3', '4', '5', '6', '7']
and this other list_B:
['2','6','7']
I would like to check this: For each element in "list_A", if it is one of the elements in "list_B"
So:
for 0 <-> are you one of these? ['2','6','7']
for 1 <-> are you one of these? ['2','6','7']
for 2 <-> are you one of these? ['2','6','7']
And at the end, I would like to come up with a "list_C" that is identical to "list_A" in terms of element count but more like a map that looks like that:
['-1', '-1', '2', '-1', '-1', '-1', '6', '7']
Which is: "-1" for every non-matching element and "self" for every matching one. Obviously I am doing this with 2 nested for each cycles, and it works:
myStateMap = []
for a in list_A:
elementString = -1
for b in list_B:
if a == b:
# Update the elementString in case of a match
elementString = a
print "\tMatch"
else:
pass
print "\tNO Match!"
# Store the elementString
myStateMap.append(elementString)
The question is: How would you optimize this? How would you make it shorter and more efficient?
You can use a list comprehension:
>>> [('-1' if item not in list_B else item) for item in list_A]
['-1', '-1', '2', '-1', '-1', '-1', '6', '7']
Use a list comprehension with a conditional expression:
[i if i in list_B else '-1' for i in list_A]
Demo:
>>> list_A = ['0', '1', '2', '3', '4', '5', '6', '7']
>>> list_B = ['2','6','7']
>>> [i if i in list_B else '-1' for i in list_A]
['-1', '-1', '2', '-1', '-1', '-1', '6', '7']
if list_B is large, you should make it a set instead:
set_B = set(list_B)
to speed up the membership testing. in on a list has linear cost (the more elements need to be scanned, the longer it takes), while the same test against a set takes constant cost (independent of the number of values in the set).
For your specific example, using a set is already faster:
>>> timeit.timeit("[i if i in list_B else '-1' for i in list_A]", "from __main__ import list_A, list_B")
1.8152308464050293
>>> timeit.timeit("set_B = set(list_B); [i if i in set_B else '-1' for i in list_A]", "from __main__ import list_A, list_B")
1.6512861251831055
but if list_A ratios list_B are different and the sizes are small:
>>> list_A = ['0', '1', '2', '3']
>>> list_B = ['2','6','8','10']
>>> timeit.timeit("[i if i in list_B else '-1' for i in list_A]", "from __main__ import list_A, list_B")
0.8118391036987305
>>> timeit.timeit("set_B = set(list_B); [i if i in set_B else '-1' for i in list_A]", "from __main__ import list_A, list_B")
0.9360401630401611
That said, in the general case it is worth your while using sets.
The quickest way to optimize is to use if a in list_B: instead of your inner loop. So the new code would look like:
for a in list_A:
if a in list_B:
myStateMap.append(a)
print '\tMatch'
else:
print '\tNO Match!'
myStateMap.append(-1)
Here's another short list comprehension example that's a little different from the others:
a=[1,2,3,4,5,6,7]
b=[2,5,7]
c=[x * (x in b) for x in a]
Which gives c = [0, 2, 0, 0, 5, 6, 7]. If your list elements are actually strings, like they seem to be, then you either get the empty string '' or the original string. This takes advantage of the implicit conversion of a boolean value (x in b) to either 0 or 1 before multiplying it by the original value (which, in the case of strings, is "repeated concatenation").
I have a list [['4', '9.012'], ['12', '24.305'], ['20', '20.078']] .
Now I want to convert it into its number equivalent
[[4, 9.012], [12, 24.305], [20, 20.078]]
I am new to python.
You can use:
from ast import literal_eval
newlist = [[literal_eval(el) for el in item] for item in mylist]
This way the type will be determined by the type required to hold that number.
If you always have pairs of integer and float,
[[int(x), float(y)] for [x, y] in mylist]
Otherwise, for more generality at the expense of type correctness,
[[float(x) for x in s] for s in mylist]
For more type correctness at the expense of clarity,
def number(x):
try:
return int(x)
except:
return float(x)
[[number(x) for x in s] for s in mylist]
lst = [['4', '9.012'], ['12', '24.305'], ['20', '20.078']]
map(lambda x: [int(x[0]), float(x[1])], lst)
>>> l = [['4', '9.012'], ['12', '24.305'], ['20', '20.078']]
>>> l1 = [ [ float(i[0]), float(i[1]) ] for i in l ]
OR
>>> l
[['4', '9.012'], ['12', '24.305'], ['20', '20.078']]
>>> def f(arg):
... return [float(arg[0]), float(arg[1])]
>>> map(f,l)
[[4.0, 9.012], [12.0, 24.305], [20.0, 20.078]]
I have the two list dictionary like this
obj1 = [mydict['obj1'],mydict['obj2'],mydict['obj3'],mydict['obj4']]
obj2 = [mydict['obj1'],mydict['obj2'],mydict['obj3'],mydict['obj4'], mydict['obj5'] ]
Now i want that
Count the number of elements in each list
Then based on whichever is greater then get that list of objects
I want a single list which conatins the above two list of(list of) dictionaries based on the higher number of elements so that i cause something like this
mylist = myfunc(objects1, objects2 )
mylist should be a list like [objects1, objects2] depending upon who has greater number of objects.
what is the best way to do that with less lines of code
Something like EDIT
mylist = sorted([obj1, obj2], key=lambda a: len(a), reverse=True)
There's no need to use a lambda function if it's just going to call a function anyway.
>>> objects1 = [1, 2, 3]
>>> objects2 = ['1', '2', '3', '4']
>>>
>>> mylist = [objects1, objects2]
>>> max(mylist, key=len)
['1', '2', '3', '4']
>>> sorted(mylist, key=len, reverse=True)
[['1', '2', '3', '4'], [1, 2, 3]]
objects1 = [1, 2, 3]
objects2 = ['1', '2', '3', '4']
mylist = [objects1, objects2]
mylist.sort(key=len, reverse=True)
print mylist
[['1', '2', '3', '4'], [1, 2, 3]]