I want to compare 2 lists, say A and B.
A = [1,2,3]
B = [3,1,2]
I want to write a function to check if all the items in A are present in B or not.
i.e. the function should return True for the example above.
Note: The items of my lists are non-hashable objects.
For this, I wrote this code:
for elem in A:
if elem not in B:
return False
return True
The code works fine.
But I want a more efficient and a more pythonic way to do this.
You may use all
all(i in B for i in A)
Example:
>>> A = [1,2,3]
>>> B = [3,1,2]
>>> all(i in B for i in A)
True
>>> B = [3,1,4]
>>> all(i in B for i in A)
False
>>>
You could use Counter, which runs in O(n) time.
from collections import Counter
A = [1,2,3]
B = [3,1,2]
print(Counter(A) == Counter(B)) # Prints True
I think using set is a good solution for this task :
In [2]: A = [1,2,3]
In [3]: B = [3,1,2]
In [5]: set(A)==set(B)
Out[5]: True
This code will have O(n) complexity for more information you can check this link
If you don't want to use set you can try to sort lists :
def cmp(A,B):
A1 = sorted(A)
B1 = sorted(B)
for item in A1:
if binary_search(B1,item)==-1:
return False
for item in B1:
if binary_search(A1,item)==-1:
return False
return True
Related
I am curious to know if there is a "pythonic" way to assign the values in a list to elements? To be clearer, I am asking for something like this:
myList = [3, 5, 7, 2]
a, b, c, d = something(myList)
So that:
a = 3
b = 5
c = 7
d = 2
I am looking for any other, better option than doing this manually:
a = myList[0]
b = myList[1]
c = myList[2]
d = myList[3]
Simply type it out:
>>> a,b,c,d = [1,2,3,4]
>>> a
1
>>> b
2
>>> c
3
>>> d
4
Python employs assignment unpacking when you have an iterable being assigned to multiple variables like above.
In Python3.x this has been extended, as you can also unpack to a number of variables that is less than the length of the iterable using the star operator:
>>> a,b,*c = [1,2,3,4]
>>> a
1
>>> b
2
>>> c
[3, 4]
Totally agree with NDevox's answer
a,b,c,d = [1,2,3,4]
I think it is also worth to mention that if you only need part of the list e.g only the second and last element from the list, you could do
_, a, _, b = [1,2,3,4]
a, b, c, d = myList
is what you want.
Basically, the function returns a tuple, which is similar to a list - because it is an iterable.
This works with all iterables btw. And you need to know the length of the iterable when using it.
One trick is to use the walrus operator in Python 3.8 so that you still have the my_list variable. And make it a one-line operation.
>>> my_list = [a:=3, b:=5, c:=7, d:=2]
>>> a
3
>>> b
5
>>> c
7
>>> d
2
>>> my_list
[3, 5, 7, 2]
PS: Using camelCase (myList) is not pythonic too.
What's new in Python 3.8 : https://docs.python.org/3/whatsnew/3.8.html
You can also use a dictionary. This was if you have more elements in a list, you don't have to waste time hardcoding that.
import string
arr = [1,2,3,4,5,6,7,8,9,10]
var = {let:num for num,let in zip(arr,string.ascii_lowercase)}
Now we can access variables in this dictionary like so.
var['a']
In Python, I know the pythonic way to check if a list is empty is
if not a:
# do things with empty list
To check if a list is not empty then, we would do:
if a:
# do things with my list
How would we check, simultaneously (as read), if two lists then are not empty?
if a and b:
# do things with my two lists
The above does not seem to work, and I'm unsure what (a and b) actually means. For a = [2], b = [1,3], (a and b) = [1,3]. What is the and operator actually doing here? If I end up reducing b = [] at some point, (a and b) = [] even though a is not empty.
Edit: My use case goes something like
while (a and b are not empty):
modify a
modify b
I would have naively thought that since if a checks if a list is not empty, if a and b would check if neither were empty, which is not the case.
It's working fine. For a = [2] and b = [1, 3], a and b is returning [1, 3] which is truthy, exactly as you would expect, because True and True is True. When you change b to [] it returns [], which is falsy, again exactly as you would expect, because True and False is False. So if a and b does exactly what you want.
What is actually happening is that and is returning the value that decided the truth of the expression. and doesn't always evaluate both sub-expressions; when the first is falsy, the whole expression is falsy and the second doesn't need to be evaluated, and therefore isn't. This is called short-circuiting. or behaves similarly (skipping evaluating the second part if the first part is truthy). Wherever and or or is able to make its decision, it returns that value.
Another way of looking at it: bool(a) and bool(a) == bool(a and b) in Python.
a and b is correct.
and returns the second argument if the first is true.
You can do this
if len(a) and len(b):
#do something
I think what you want is
>>> a, b = list(), list()
>>> while not (a and b):
... a.append(1)
... b.append(2)
...
>>> a, b
([1], [2])
>>>
and operator is compare two boolean values
bool([]) == False
so [] and b always return []; no matter what b is.
To build on kindall's answer - you could also concatenate the lists before doing the truthiness test:
a = []
b = []
c = [1]
bool(a + b) # False
bool(a + b + c) # True
Of course this would come with a performance penalty if you were doing something where that mattered.
In the following code I create a list of three objects and use the variable a,b,c to store the first, second and then the third object by their id's but when I try to store the third object in variable c, it stores a list of the second and third object.
class Obj1():
id='obj1'
class Obj2():
id='obj2'
class Obj3():
id='obj3'
list1=[Obj1(),Obj2(),Obj3()]
a=list1[id=="obj1"]
print a
b=list1[id!='obj1']
print b
c=list1[id!='obj1'and id!='obj2':]
print c
When I run this code I get :
<__main__.Obj1 instance at 0x02AD5DA0>
<__main__.Obj2 instance at 0x02AD9030>
[<__main__.Obj2 instance at 0x02AD9030>, <__main__.Obj3 instance at 0x02AD90A8>]
why does variable c contain two objects?
Using a dictionary is probably the best idea in this case, as mentioned by Medhat. However, you can do things in a similar way to what you attempted using list comprehensions:
a = [e for e in list1 if e.id == "obj1"]
print a
b = [e for e in list1 if e.id != "obj1"]
print b
c = [e for e in list1 if e.id != "obj1" and e.id != "obj2"]
# Or:
# [e for e in list1 if e.id not in ("obj1", "obj2")]
print c
You should use a dictionary instead:
obj1 = Obj1()
obj2 = Obj2()
obj3 = Obj3()
list1 = {obj1.id: obj1, obj2.id: obj2, obj3.id: obj3}
Then access your objects like this:
a = list1[obj1.id]
b = list1[obj2.id]
c = list1[obj3.id]
id!='obj1'and id!='obj2' returns true which equals 1 in Python, that is to say, c=list1[id!='obj1'and id!='obj2':] equals c=list1[1:] , which of course has two objects.
BTW, id is the name of a built-in function. Please avoid using it as a name of varible.
Your list1 contains 3 elements:
>>> list1
[<__main__.Obj1 instance at 0x103437440>, <__main__.Obj2 instance at 0x1034377a0>, <__main__.Obj3 instance at 0x103437320>]
>>> id!='obj1'
True
>>> id!='obj2'
True
>>> True and True
True
>>> list1[True:]
[<__main__.Obj2 instance at 0x1034377a0>, <__main__.Obj3 instance at 0x103437320>]
>>>
True is 1 and False is 0 index:
Here is the example:
>>> ls = [1,2]
>>> ls[True]
2
>>> ls[False]
1
>>>
So, list[True:] is equal to list[1:] which is from first element till last.
In your case the the last two elements in the list1.
I create a list of dictionaries like this:
list = []
for i in xrange(4):
list.append({})
list[i]['a'] = False
Now after a while, I want to (using a single line of code) see if any of the 'a' values are True.
I have tried:
anyTrue = True in list # always returns false
anyTrue = True in list[:]['a']
Is there such a convenient way of doing this?
Thanks!
Using any with generator expression:
>>> lst = []
>>>
>>> for i in xrange(4):
... lst.append({})
... lst[i]['a'] = False
...
>>> any(d['a'] for d in lst)
False
>>> lst[1]['a'] = True
>>> any(d['a'] for d in lst)
True
BTW, don't use the list as a variable name. It shadows builtin function/type list.
You can use any and a generator expression:
if any(x['a'] for x in list):
# Do stuff
See a demonstration below:
>>> lst = []
>>> for i in xrange(4):
... lst.append({})
... lst[i]['a'] = False
...
>>> any(x['a'] for x in lst)
False
>>> lst[2]['a'] = True # Set an 'a' value to True
>>> any(x['a'] for x in lst)
True
>>>
Also, you should refrain from naming a variable list. Doing so overshadows the built-in.
I have three collection.deques and what I need to do is to iterate over each of them and perform the same action:
for obj in deque1:
some_action(obj)
for obj in deque2:
some_action(obj)
for obj in deque3:
some_action(obj)
I'm looking for some function XXX which would ideally allow me to write:
for obj in XXX(deque1, deque2, deque3):
some_action(obj)
The important thing here is that XXX have to be efficient enough - without making copy or silently using range(), etc. I was expecting to find it in built-in functions, but I found nothing similar to it so far.
Is there such thing already in Python or I have to write a function for that by myself?
Depending on what order you want to process the items:
import itertools
for items in itertools.izip(deque1, deque2, deque3):
for item in items:
some_action(item)
for item in itertools.chain(deque1, deque2, deque3):
some_action(item)
I'd recommend doing this to avoid hard-coding the actual deques or number of deques:
deques = [deque1, deque2, deque3]
for item in itertools.chain(*deques):
some_action(item)
To demonstrate the difference in order of the above methods:
>>> a = range(5)
>>> b = range(5)
>>> c = range(5)
>>> d = [a, b, c]
>>>
>>> for items in itertools.izip(*d):
... for item in items:
... print item,
...
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
>>>
>>> for item in itertools.chain(*d):
... print item,
...
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
>>>
The answer is in itertools
itertools.chain(*iterables)
Make an iterator that returns elements from the first iterable until
it is exhausted, then proceeds to the
next iterable, until all of the
iterables are exhausted. Used for
treating consecutive sequences as a
single sequence. Equivalent to:
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
Call me crazy, but why is using itertools thought to be necessary? What's wrong with:
def perform_func_on_each_object_in_each_of_multiple_containers(func, containers):
for container in containers:
for obj in container:
func(obj)
perform_func_on_each_object_in_each_of_multiple_containers(some_action, (deque1, deque2, deque3)
Even crazier: you probably are going to use this once. Why not just do:
for d in (deque1, deque2, deque3):
for obj in d:
some_action(obj)
What's going on there is immediately obvious without having to look at the code/docs for the long-name function or having to look up the docs for itertools.something()
Use itertools.chain(deque1, deque2, deque3)
How about zip?
for obj in zip(deque1, deque2, deque3):
for sub_obj in obj:
some_action(sub_obj)
Accepts a bunch of iterables, and yields the contents for each of them in sequence.
def XXX(*lists):
for aList in lists:
for item in aList:
yield item
l1 = [1, 2, 3, 4]
l2 = ['a', 'b', 'c']
l3 = [1.0, 1.1, 1.2]
for item in XXX(l1, l2, l3):
print item
1
2
3
4
a
b
c
1.0
1.1
1.2
It looks like you want itertools.chain:
"Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. Used for treating consecutive sequences as a single sequence."
If I understand your question correctly, then you can use map with the first argument set to None, and all the other arguments as your lists to iterate over.
E.g (from an iPython prompt, but you get the idea):
In [85]: p = [1,2,3,4]
In [86]: q = ['a','b','c','d']
In [87]: f = ['Hi', 'there', 'world', '.']
In [88]: for i,j,k in map(None, p,q,f):
....: print i,j,k
....:
....:
1 a Hi
2 b there
3 c world
4 d .
I would simply do this :
for obj in deque1 + deque2 + deque3:
some_action(obj)
>>> a = [[],[],[]]
>>> b = [[],[],[]]
>>> for c in [*a,*b]:
c.append("derp")
>>> a
[['derp'], ['derp'], ['derp']]
>>> b
[['derp'], ['derp'], ['derp']]
>>>