Python list comprehension and do action - python

Is it possible to do an action with the item in a list comprehension?
Example:
list = [1, 2, 3]
list_filtered = [ i for i in list if i == 3 AND DO SOMETHING WITH I]
print (list_filtered)
For example if I want to remove the '3' how would I go about it? Logic says that it's something like:
list = [1, 2, 3]
list_filtered = [ i for i in list if i == 3 && list.remove(i) ]
print (list_filtered)
I can't seem to make Python perform an action with that 'i' with any syntax that I tried. Can someone please elaborate?
EDIT: Sorry, the explanation might not be clear enough. I know how to iterate and create the new list. I want to create "list_filtered" and remove that value from "list" if it fits the "IF" statement.
Practically I need the following:
list = [1, 2, 3]
list_filtered = [ i for i in list if i == 3 && list.remove(i) ]
print (list_filtered)
# output >> [3]
print (list)
# output >> [1, 2]
I hope the above makes it more clear. Also, please note that my question is if this can be done in the list comprehension specifically. I know how to do it with additional code. :)
EDIT2: Apparently what I wanted to do isn't possible and also isn't advisable (which is the reason it isn't possible). It seemed like a logical thing that I just didn't know how to do. Thanks guys :)

If you're just trying to remove 3 you could do:
list_filtered=[i for i in list if i != 3]
print(list_filtered) # [1,2]
This will remove all values that are not equal to 3.
Alternatively if you wanted to do something like increment all the items in the list you would do:
[i+1 for i in list]
>>> [2,3,4]
Using a function on every item of the list would look like:
[float(i) for i in list]
>>> [1.0, 2.0, 3.0]
You can do ternary statements:
[i if i<3 else None for i in list]
>>>[1, 2, None]
and a whole lot more...
Here is more documentation on list comprehensions.
Given your new updates, I would try something like:
list_filtered=[list.pop(list.index(3))]
Then list_filtered would be [3] and list would be [1,2] as your specified.

Your miss understand the purpose of list comprehension. List comprehension should be used to create a list, not to use its side effects. Further more, as Leijot has already mentioned, you should never modify a container while iterating over it.
If you want to filter out certain elements in a list using list comprehension, use an if statement:
>>> l = [1, 2, 3]
>>> l_filtered = [i for i in l if i != 3]
>>> l_filtered
[1, 2]
>>>
Alternatively, you can use the builtin function filter():
>>> l = [1, 2, 3]
>>> l_filtered = list(filter(lambda x: x != 3, l))
>>> l_filtered
[1, 2]
>>>
Edit: Based on your most recent edit, what your asking is absolutely possible. Just use two different list comprehensions:
>>> l = [1, 2, 3]
>>> l_filtered = [i for i in l if i == 3]
>>> l_filtered
[3]
>>> l = [i for i in l if i != 3] # reassigning the value of l to a new list
>>> l
[1, 2]
>>>

Maybe you want to provide a different example of what you're trying to do since you shouldn't remove elements from a list while you're iterating over it. Since list comprehensions create another list anyway, you could accomplish the same thing as your example by building a list that omits certain elements. If your goal is to create a list that excludes the value 3, to slightly modify your example:
l = [1, 2, 3]
list_filtered = [ i for i in l if i != 3 ]
l remains unaltered, but list_filtered now contains [1, 2].

Related

Filter nested list with list comprehension based on other list in python

I have two lists like this:
a = [[1,2,3],[2,3,4],[5,6,7],[7,8,9]]
b = [1,2]
I would now like to filter list a, to keep only the items which contain the items in list b. So the desired output would look like this:
[[1,2,3],[2,3,4]]
I have tried some nested list comprehensions, which I could think of, but could not get the desired output. Any advice is appreciated.
you could try something like this :
print([i for i in a if any(map(i.__contains__,b))])
>>> [[1, 2, 3], [2, 3, 4]]
I would try something like this:
a = [[1,2,3],[2,3,4],[5,6,7],[7,8,9]]
b = [1,2]
result = [lst for lst in a if any(x in lst for x in b)]
A combination of list comprehension and sets would yield the wanted result. Note; I assume that repeated items and ordering is not of interest, if this is the case - a set won't work since it ignores ordering and only allows unique items.
A simple list comprehension would do, like below
filter_items = set(filter_items)
[sublist for sublist in original_list if not set(sublist).isdisjoint(filter_items)]
There's mainly one interesting part of this list comprehension, namely the if not set(sublist).isdisjoint(filter_items). Here you only keep the sublist if the set of sublist is not disjoint of the set filter_items i.e. none of the filter_items is in the sublist.
for your given example the provided answer would yield the following:
>>> a = [[1,2,3],[2,3,4],[5,6,7],[7,8,9]]
>>> b = set([1,2])
>>> [sublist for sublist in a if not set(sublist).isdisjoint(b)]
[[1, 2, 3], [2, 3, 4]]
Using a set approach the in can be mimic with an intersection:
a = [[1,2,3],[2,3,4],[5,6,7],[7,8,9]]
b = [1,2]
b_as_set = set(b)
out = [l for l in a if b_as_set.intersection(l)]
# [[1, 2, 3], [2, 3, 4]]
I would try something like this.
a = [[1,2,3],[2,3,4],[5,6,7],[7,8,9]]
b = [1,2]
print([lst for lst in a if any([item in b for item in lst])])

remove non repeating characters from a list

I am trying to remove non repeating characters from a list in python. e.g list = [1,1,2,3,3,3,5,6] should return [1,1,3,3].
My initial attempt was:
def tester(data):
for x in data:
if data.count(x) == 1:
data.remove(x)
return data
This will work for some inputs, but for [1,2,3,4,5], for example, it returns [2,4]. Could someone please explain why this occurs?
l=[1,1,2,3,3,3,5,6]
[x for x in l if l.count(x) > 1]
[1, 1, 3, 3, 3]
Adds elements that appear at least twice in your list.
In your own code you need to change the line for x in data to for x in data[:]:
Using data[:] you are iterating over a copy of original list.
There is a linear time solution for that:
def tester(data):
cnt = {}
for e in data:
cnt[e] = cnt.get(e, 0) + 1
return [x for x in data if cnt[x] > 1]
This is occurring because you are removing from a list as you're iterating through it. Instead, consider appending to a new list.
You could also use collections.Counter, if you're using 2.7 or greater:
[a for a, b in collections.Counter(your_list).items() if b > 1]
Another linear solution.
>>> data = [1, 1, 2, 3, 3, 3, 5, 6]
>>> D = dict.fromkeys(data, 0)
>>> for item in data:
... D[item] += 1
...
>>> [item for item in data if D[item] > 1]
[1, 1, 3, 3, 3]
You shouldn't remove items from a mutable list while iterating over that same list. The interpreter doesn't have any way to keep track of where it is in the list while you're doing this.
See this question for another example of the same problem, with many suggested alternative approaches.
you can use the list comprehention,just like this:
def tester(data):
return [x for x in data if data.count(x) != 1]
it is not recommended to remove item when iterating

Remove duplicates from list, including original matching item

I tried searching and couldn't find this exact situation, so apologies if it exists already.
I'm trying to remove duplicates from a list as well as the original item I'm searching for. If I have this:
ls = [1, 2, 3, 3]
I want to end up with this:
ls = [1, 2]
I know that using set will remove duplicates like this:
print set(ls) # set([1, 2, 3])
But it still retains that 3 element which I want removed. I'm wondering if there's a way to remove the duplicates and original matching items too.
Use a list comprehension and list.count:
>>> ls = [1, 2, 3, 3]
>>> [x for x in ls if ls.count(x) == 1]
[1, 2]
>>>
Here is a reference on both of those.
Edit:
#Anonymous made a good point below. The above solution is perfect for small lists but may become slow with larger ones.
For large lists, you can do this instead:
>>> from collections import Counter
>>> ls = [1, 2, 3, 3]
>>> c = Counter(ls)
>>> [x for x in ls if c[x] == 1]
[1, 2]
>>>
Here is a reference on collections.Counter.
If items are contigious, then you can use groupby which saves building an auxillary data structure in memory...:
from itertools import groupby, islice
data = [1, 2, 3, 3]
# could also use `sorted(data)` if need be...
new = [k for k, g in groupby(data) if len(list(islice(g, 2))) == 1]
# [1, 2]

python list comprehension and extend() [duplicate]

This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Closed 5 months ago.
Working my way into Python (2.7.1)
But failing to make sense (for hours) of this:
>>> a = [1, 2]
>>> b = [3, 4]
>>>
>>> a.extend([b[0]])
>>> a
[1, 2, 3]
>>>
>>> a.extend([b[1]])
>>> a
[1, 2, 3, 4]
>>>
>>> m = [a.extend([b[i]]) for i in range(len(b))] # list of lists
>>> m
[None, None]
The first two extends work as expected, but when compacting the same in a list comprehension it fails.
What am i doing wrong?
extend modifies the list in-place.
>>> [a + b[0:i] for i in range(len(b)+1)]
[[1, 2], [1, 2, 3], [1, 2, 3, 4]]
list.extend() extends a list in place. Python standard library methods that alter objects in-place always return None (the default); your list comprehension executed a.extend() twice and thus the resulting list consists of two None return values.
Your a.extend() calls otherwise worked just fine; if you were to print a it would show:
[1, 2, 3, 4, 3, 4]
You don't see the None return value in the Python interpreter, because the interpreter never echoes None results. You could test for that explicitly:
>>> a = []
>>> a.extend(['foo', 'bar']) is None
True
>>> a
['foo', 'bar']
the return value of extend is None.
extend function extends the list with the value you've provided in-place and returns None. That's why you have two None values in your list. I propose you rewrite your comprehension like so:
a = [1, 2]
b = [3, 4]
m = [a + [v] for v in b] # m is [[1,2,3],[1,2,4]]
For python lists, methods that change the list work in place and return None. This applies to extendas well as to append, remove, insert, ...
In reply to an older question, I sketched an subclass of list that would behave as you expected list to work.
Why does [].append() not work in python?
This is intended as educational. For pros and cons.. look at the comments to my answer.
I like this for the ability of chaining methods and working in a fluent style, e.g. then something like
li = FluentList()
li.extend([1,4,6]).remove(4).append(7).insert(1,10).reverse().sort(key=lambda x:x%2)
would be possible.
a.extend() returns None.
You probably want one of these:
>>> m = a + b
>>> m
[1, 2, 3, 4]
>>> a.extend(b)
>>> a
[1, 2, 3, 4]
Aside from that, if you want to iterate over all elements of a list, you just can do it like that:
m = [somefunction(element) for element in somelist]
or
for element in somelist:
do_some_thing(element)
In most cases there is no need to go over the indices.
And if you want to add just one element to a list, you should use somelist.append(element) instead of `somelist.extend([element])

Python: identical items in list are treated as the same

I have a nested list in python. Each item the second list is also a nested list. My goal is to duplicate one list, insert it right at the same index, and then modify each one. So, example of start condition:
myList = [[first_list], [[element_1], [element_2, element_3], [duplicate_me]]]
Duplication/insertion at myList[1][2]:
myList = [[first_list], [[element_1], [element_2, element_3], [duplicate_me], [duplicate_me]]]
This all works fine. However, when I run the append code:
myList[1][2].append(new_element)
It appends the new element to both duplicates, like this:
myList = [[first_list], [[element_1], [element_2, element_3], [duplicate_me, new_element], [duplicate_me, new_element]]]
Is there something bizarre going on in the way the elements are called or indexed? I see a potential workaround (calling the item to be duplicated to a working variable, modifying it there, and then inserting it at the same point), but that seems needlessly complex.
Thanks!
myList[1][2] and myList[1][3] don't just have the same values, they are actually the same list. You are looking at the same area in memory when you read both of them. So, when you change one, you are changing the other, because both are actually the exact same thing! Instead doing what you do to duplicate the list, you should make a copy of it.
Here's an example of the problem from a python shell:
>>> mylist = [1, 2, 3]
>>> newlist = mylist
>>> newlist.append(4)
>>> newlist
[1, 2, 3, 4]
>>> mylist
[1, 2, 3, 4]
A common way to fix this is to use one of these tricks:
>>> mylist = [1, 2, 3]
>>> newlist = mylist[:] # OR :
>>> newlist = [x for x in mylist]
>>> newlist.append(4)
>>> newlist
[1, 2, 3, 4]
>>> mylist
[1, 2, 3]
The second and third lines will create a copy of the list. I tend to combine the two like this, if I need to copy a 2D list:
>>> newlist = [x[:] for x in mylist]
Use one of these to create your duplicate, and all will be well again.
You are most likely not duplicating the target list (*[duplicate_me]*), but rather simply appending a duplicated reference to the same list into myList.
You need to copy the list before adding it to myList. One simple way is to call the list constructor passing in the original [duplicate_me]

Categories