I have list_a and list_b. Both of these lists have items in an order.
Each time I encounter a 0 in list_b, I want to remove from list_a AND list_b the entry associated with that index. I am not sure how to do that.
# Before modification
list_a = [ '2019', '2020', '2021', '2022', '2023' ]
list_b = [ 40, 0, 30, 0, 9 ]
#After modification
list_a = [ '2019', '2021', '2023' ]
list_b = [ 40, 30, 9 ]
Any clue on how to approach this?
Good use case for itertools.compress and filter:
list_a[:] = compress(list_a, list_b)
list_b[:] = filter(None, list_b)
Try it online!
There are probably 100 ways to do this, and I'm sure you'll get diverse responses. If you're interested in learning this, you should try a couple...
Use a for-loop over an index. Before the loop, make 2 new lists like list_a_new, list_b_new and then use the for loop to loop over the index of the original list_b. test the object you get out. Use a conditional statement. If the object is not zero, get the items from the original lists at the same index position and add it to both of the new results by append()
Use a list comprehension for both of the new lists and use enumerate(list_b) inside to get the same type of info and see if you can do a list comprehension for both new lists
Make a "mask". numpy can do this or you can make your own, perhaps with a list comprehension again over list_b to make a mask of booleans like [False, True, False, True, ...] Use that mask as the basis of another list comprehension to get new_a and new_b
Try a couple and edit your post if you are stuck! You'll improve your skills.
As mentioned by other users there are many ways
list_a2 = [list_a[i] for i in range(len(list_b)) if list_b[i]!=0]
list_b2 = [list_b[i] for i in range(len(list_b)) if list_b[i]!=0]
Here's a solution using a traditional for-loop to iterate over items from list_a paired (using zip()) with items from list_b:
new_a = []
new_b = []
for a, b in zip(list_a, list_b):
if b != 0:
new_a.append(a)
new_b.append(b)
Or you could use a couple of list comprehensions:
new_a = [a for a, b in zip(list_a, list_b) if b != 0]
new_b = [b for b in list_b if b != 0]
You do it all in one line but readability suffers:
new_a, new_b = map(list, zip(*((a, b) for a, b in zip(list_a, list_b) if b != 0)))
If you don't mind modifying your original lists it becomes slightly less unreadable:
list_a[:], list_b[:] = zip(*((a, b) for a, b in zip(list_a, list_b) if b != 0))
Related
Now I know that it is not safe to modify the list during an iterative looping. However, suppose I have a list of strings, and I want to strip the strings themselves. Does replacement of mutable values count as modification?
See Scope of python variable in for loop for a related problem: assigning to the iteration variable does not modify the underlying sequence, and also does not impact future iteration.
Since the loop below only modifies elements already seen, it would be considered acceptable:
a = ['a',' b', 'c ', ' d ']
for i, s in enumerate(a):
a[i] = s.strip()
print(a) # -> ['a', 'b', 'c', 'd']
Which is different from:
a[:] = [s.strip() for s in a]
in that it doesn't require the creation of a temporary list and an assignment of it to replace the original, although it does require more indexing operations.
Caution: Although you can modify entries this way, you can't change the number of items in the list without risking the chance of encountering problems.
Here's an example of what I mean—deleting an entry messes-up the indexing from that point on:
b = ['a', ' b', 'c ', ' d ']
for i, s in enumerate(b):
if s.strip() != b[i]: # leading or trailing whitespace?
del b[i]
print(b) # -> ['a', 'c '] # WRONG!
(The result is wrong because it didn't delete all the items it should have.)
Update
Since this is a fairly popular answer, here's how to effectively delete entries "in-place" (even though that's not exactly the question):
b = ['a',' b', 'c ', ' d ']
b[:] = [entry for entry in b if entry.strip() == entry]
print(b) # -> ['a'] # CORRECT
See How to remove items from a list while iterating?.
It's considered poor form. Use a list comprehension instead, with slice assignment if you need to retain existing references to the list.
a = [1, 3, 5]
b = a
a[:] = [x + 2 for x in a]
print(b)
One more for loop variant, looks cleaner to me than one with enumerate():
for idx in range(len(list)):
list[idx]=... # set a new value
# some other code which doesn't let you use a list comprehension
Modifying each element while iterating a list is fine, as long as you do not change add/remove elements to list.
You can use list comprehension:
l = ['a', ' list', 'of ', ' string ']
l = [item.strip() for item in l]
or just do the C-style for loop:
for index, item in enumerate(l):
l[index] = item.strip()
The answer given by Ignacio Vazquez-Abrams is really good. It can be further illustrated by this example. Imagine that:
A list with two vectors is given to you.
You would like to traverse the list and reverse the order of each one of the arrays.
Let's say you have:
v = np.array([1,2,3,4])
b = np.array([3,4,6])
for i in [v, b]:
i = i[::-1] # This command does not reverse the string.
print([v,b])
You will get:
[array([1, 2, 3, 4]), array([3, 4, 6])]
On the other hand, if you do:
v = np.array([1,2,3,4])
b = np.array([3,4,6])
for i in [v, b]:
i[:] = i[::-1] # This command reverses the string.
print([v,b])
The result is:
[array([4, 3, 2, 1]), array([6, 4, 3])]
No you wouldn't alter the "content" of the list, if you could mutate strings that way. But in Python they are not mutable. Any string operation returns a new string.
If you had a list of objects you knew were mutable, you could do this as long as you don't change the actual contents of the list.
Thus you will need to do a map of some sort. If you use a generator expression it [the operation] will be done as you iterate and you will save memory.
You can do something like this:
a = [1,2,3,4,5]
b = [i**2 for i in a]
It's called a list comprehension, to make it easier for you to loop inside a list.
It is not clear from your question what the criteria for deciding what strings to remove is, but if you have or can make a list of the strings that you want to remove , you could do the following:
my_strings = ['a','b','c','d','e']
undesirable_strings = ['b','d']
for undesirable_string in undesirable_strings:
for i in range(my_strings.count(undesirable_string)):
my_strings.remove(undesirable_string)
which changes my_strings to ['a', 'c', 'e']
In short, to do modification on the list while iterating the same list.
list[:] = ["Modify the list" for each_element in list "Condition Check"]
example:
list[:] = [list.remove(each_element) for each_element in list if each_element in ["data1", "data2"]]
Something I just discovered - when looping over a list of mutable types (such as dictionaries) you can just use a normal for loop like this:
l = [{"n": 1}, {"n": 2}]
for d in l:
d["n"] += 1
print(l)
# prints [{"n": 2}, {"n": 1}]
This question already has answers here:
Comparing two lists and only printing the differences? (XORing two lists)
(6 answers)
Closed 2 years ago.
I've got two lists, for example:
a = ['hello','world']
b = ['hello','world','im','steve']
If I want to create a third list that only contains elements NOT in both:
c = ['im','steve']
How do I do this if the order of the elements IS important? I know I can use sets but they keep throwing out the order of my lists. I could use ' '.join(list) to convert them to strings but not sure how to do this operation in that format either.
You can concatenate the lists and use list comprehension:
a = ['hello','world']
b = ['hello','world','im','steve']
final_vals = [i for i in a+b if i not in a or i not in b]
Output:
['im', 'steve']
Option 1: set method (recommended)
Sets have a symmetric_difference method that exclusively return elements from either a or b. Order can be preserved with a list comprehension for a concatenated list a + b.
comp = set(a).symmetric_difference(b)
[x for x in a + b if x in comp]
# ['im', 'steve']
Option 2: pathlib method
For reference, another way to diff two lists might be with pathlib.Path.relative_to method:
import pathlib
p = pathlib.Path(*b)
r = p.relative_to(*a)
list(r.parts)
# ['im', 'steve']
Note: b is the longer list. This option is potentially less efficient than a simple list comprehension.
Add two lists together and minus the intersection part if it shows in the new list. Order is preserved.
c = a + b
for v in set(a).intersection(set(b)):
while v in c:
c.remove(v)
a = ['hello','world']
b = ['hello','world','im','steve']
a = set(a)
b = set(b)
print(a.symmetric_difference(b))
This code print elements that are only in one of the tables.
Look here:
https://learnpython.org/en/Sets
You could also just create a function that filters elements from l1 that don't exist in l2, and call it twice with the arguments flipped:
a = ['hello','world', 'foo']
b = ['hello','world','im','steve']
def difference(l1, l2):
return list(filter(lambda x: x not in l2, l1))
print(difference(a, b) + difference(b, a))
# ['foo', 'im', 'steve']
If you don't wish to use filter(), a simple list comprehension like this also works:
[item for item in l1 if item not in l2]
The question is not very clear, indeed, and probably you're good with #Ajax1234 's answer, but here's another "take" on it.
If you wanna compare positions (kind of what a bit-wise XOR would do) you can do something like getting the shortest list, iterate checking position by position with the longest list (check the same position in the longest list matches the word in the shortest list) and then add the remainder (the "unwalked" part of the longest list). Something like the following:
a = ['hello', 'world']
b = ['hello', 'world', 'im', 'steve']
min_list = a if len(a) < len(b) else b
max_list = b if len(b) > len(a) else a
results = []
for i, item in enumerate(min_list):
# Iterate through the shortest list to avoid IndexError(s)
if min_list[i] != max_list[i]:
results.append(min_list[i])
results.append(max_list[i])
results.extend(max_list[i + 1:])
print(results)
# Prints: ['im', 'steve']
However, then you have the problem of what to do if the same positions don't match. I mean... What to do in that case? In the code above, I just added both entries to the results list, which means for the following inputs:
a = ['hello', 'foo']
b = ['hello', 'world', 'im', 'steve']
would output:
>>> ['foo', 'world', 'im', 'steve']
(notice both foo from list a and world from list b have been added)
Using standard for loop to check for items not in one or the other list (may be more understandable than list comprehension):
a = ['hello','world', 'foo']
b = ['hello','world','im','steve']
c = a+b
ans = []
for i in c:
if i not in a or i not in b:
ans.append(i)
print(ans)
Output:
['foo', 'im', 'steve']
I recommend, using ^ operator with sets, like set(a) ^ set(b), Example (demo):
>>> a = ['hello','world']
>>> b = ['hello','world','im','steve']
>>> set(a) ^ set(b)
{'steve', 'im'}
>>> sorted(set(a) ^ set(b),key=max([a,b],key=len).index)
['im', 'steve']
>>>
https://docs.python.org/2/library/stdtypes.html#frozenset.symmetric_difference
I'd like to know how I can easily generate a list based on the values/order of two other lists:
list_a = ['web1','web2','web3','web1','web4']
list_b = ['web2','web4','web1','web5','web1']
I'd like to retrieve the "list_b" list ordered by value from "list_a":
final = ['web1','web2','web1','web4','web5']
If an entry exist on list_b but not on list_a, then the value is appended to the list at the end.
I'm not sure where to start, my initial thinking was to retrieve all the indexes with enum [i for i, x in enumerate(mylist) if x==value], then sort the list, but I'm having hard time managing entries with multiples index (eg: web1) . Just wondering if you guys are thinking about an easy way to achieve this ?
An extremely simplistic way would be to just iterate over list_a, and should you find each element in list_b you remove it and append it to a list. Then after iterating all that remains in list_b are the elements that you need to add to the end of your list.
list_a = ['web1','web2','web3','web1','web4']
list_b = ['web2','web4','web1','web5','web1']
front = []
for ele in list_a:
if ele in list_b:
front.append(ele)
list_b.remove(ele)
final = front + list_b
print(final)
Outputs:
['web1', 'web2', 'web1', 'web4', 'web5']
Another trickier way would be to use collections.Counter and a few list comprehensions, leveraging the set intersection and difference of the counters.
from collections import Counter
cnt_a, cnt_b = Counter(list_a), Counter(list_b)
intersct = (cnt_a & cnt_b)
diff = (cnt_b - cnt_a)
final = [a for a in list_a if a in intersct] + [b for b in list_b if b in diff]
In Python you can use a.intersection(b) to find the items common to both sets.
Is there a way to do the disjoint opposite version of this? Items that are not common to both a and b; the unique items in a unioned with the unique items in b?
You are looking for the symmetric difference; all elements that appear only in set a or in set b, but not both:
a.symmetric_difference(b)
From the set.symmetric_difference() method documentation:
Return a new set with elements in either the set or other but not both.
You can use the ^ operator too, if both a and b are sets:
a ^ b
while set.symmetric_difference() takes any iterable for the other argument.
The output is the equivalent of (a | b) - (a & b), the union of both sets minus the intersection of both sets.
a={1,2,4,5,6}
b={5,6,4,9}
c=(a^b)&b
print(c) # you got {9}
The best way is a list comprehension.
a = [ 1,2,3,4]
b = [ 8,7,9,2,1]
c = [ element for element in a if element not in b]
d = [ element for element in b if element not in a]
print(c)
# output is [ 3,4]
print(d)
# output is [8,7,9]
You can join both lists
Try this code for (set(a) - intersection(a&b))
a = [1,2,3,4,5,6]
b = [2,3]
for i in b:
if i in a:
a.remove(i)
print(a)
the output is [1,4,5,6]
I hope, it will work
e, f are two list you want to check disjoint
a = [1,2,3,4]
b = [8,7,9,2,1]
c = []
def loop_to_check(e,f):
for i in range(len(e)):
if e[i] not in f:
c.append(e[i])
loop_to_check(a,b)
loop_to_check(b,a)
print(c)
## output is [3,4,8,7,9]
This loops around to list and returns the disjoint list
I am trying to find all the elements that are in list A and not in list B.
I thought something like newList = list(set(a) & !set(b)) or newList = list(set(a) & (not set(b))) would work, but it's not.
If there a better way to achieve what I'm trying to do other than this?
newList = []
for item in a:
if item not in b:
newList.append(item)
Also important, it needs to be done in Python 2.6
You're looking for the set difference:
newList = list(set(a).difference(b))
Alternatively, use the minus operator:
list(set(a) - set(b))
Did you try
list(set(a) - set(b))
Here is a list of all Python set operations.
But this unnecessarily creates a new set for b. As #phihag mentions, difference method would prevent this.
If you care about maintaining order:
def list_difference(a, b):
# returns new list of items in a that are not in b
b = set(b)
return [x for x in a if x not in b]
>>> list1 = [1,2,3,4,5]
>>> list2 = [4,5,6,7,8]
>>> print list(set(list1)-set(list2))
[1, 2, 3]