I'm solving a Python challenge which I answered it like this:
def areEquallyStrong(yourLeft, yourRight, friendsLeft, friendsRight):
return (yourLeft == friendsLeft or yourLeft == friendsRight)
and (yourRight == friendsLeft or yourRight == friendsRight)
I wonder if its equivalent to
def areEquallyStrong(yourLeft, yourRight, friendsLeft, friendsRight):
return {yourLeft, yourRight} == {friendsLeft, friendsRight}
I don't precisely know what the {} == {} is doing under the hood and how it's being compared.
Using the builtin type function is useful here:
>>> type({})
<class 'dict'>
>>> type({1, 2})
<class 'set'>
We can conclude that {} in your case is not a dictionary, but a set. A set is an unordered sequence of values, in which each element is unique (if you do print({2, 2}) the output will be {2}). {1, 2} == {1, 2} does a set comparison. Basically, it checks if the two sets were the same if they were ordered.
But, the two code snippets won't give the same result, because, in the first you are checking if both yourLeft and yourRight equal one of friendsLeft and friendsRight, and in the second you aren't. You would be better off with putting friendsLeft and friendsRight in a list and checking if both yourLeft and yourRight are present in that list:
def areEquallyStrong(yourLeft, yourRight, friendsLeft, friendsRight):
return yourLeft in [friendsLeft, friendsRight] and yourRight in [friendsLeft, friendsRight]
In python, {item1, item2} creates a set. A set object is an unordered collection of distinct objects. Duplicated items will be removed when creating a set or adding items to a set.
Equivalence comparison between two sets yields True if they contains exactly the same elements.
You can try it like:
>>> s1 = {1, 2, 3, 1}
>>> s2 = {3, 2, 1}
>>> s1
{1, 2, 3}
>>> s2
{1, 2, 3}
>>> s1 == s2
True
Notes:
To be precise, the objects in a set must be hashable.
While s = {item1, item2} creates a set, s = {} does not create an empty set. Instead, it creates an empty dict. To create an empty set, use s = set().
{} == {} is a set comparison. Python’s built-in set type are unordered and unique. Two sets are equal if and only if every element of each set is contained in the other
you can use max and min to check these
def areEquallyStrong(yourLeft, yourRight, friendsLeft, friendsRight):
if max(yourLeft,yourRight) == max(friendsLeft,friendsRight) and
min(yourLeft,yourRight) == min(friendsLeft,friendsRight):
return True
else:
return False
Related
a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]
a & b should be considered equal, because they have exactly the same elements, only in different order.
The thing is, my actual lists will consist of objects (my class instances), not integers.
O(n): The Counter() method is best (if your objects are hashable):
def compare(s, t):
return Counter(s) == Counter(t)
O(n log n): The sorted() method is next best (if your objects are orderable):
def compare(s, t):
return sorted(s) == sorted(t)
O(n * n): If the objects are neither hashable, nor orderable, you can use equality:
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
You can sort both:
sorted(a) == sorted(b)
A counting sort could also be more efficient (but it requires the object to be hashable).
>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True
If you know the items are always hashable, you can use a Counter() which is O(n)
If you know the items are always sortable, you can use sorted() which is O(n log n)
In the general case you can't rely on being able to sort, or has the elements, so you need a fallback like this, which is unfortunately O(n^2)
len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)
If you have to do this in tests:
https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual
assertCountEqual(first, second, msg=None)
Test that sequence first contains the same elements as second, regardless of their order. When they don’t, an error message listing the differences between the sequences will be generated.
Duplicate elements are not ignored when comparing first and second. It verifies whether each element has the same count in both sequences. Equivalent to: assertEqual(Counter(list(first)), Counter(list(second))) but works with sequences of unhashable objects as well.
New in version 3.2.
or in 2.7:
https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual
Outside of tests I would recommend the Counter method.
The best way to do this is by sorting the lists and comparing them. (Using Counter won't work with objects that aren't hashable.) This is straightforward for integers:
sorted(a) == sorted(b)
It gets a little trickier with arbitrary objects. If you care about object identity, i.e., whether the same objects are in both lists, you can use the id() function as the sort key.
sorted(a, key=id) == sorted(b, key==id)
(In Python 2.x you don't actually need the key= parameter, because you can compare any object to any object. The ordering is arbitrary but stable, so it works fine for this purpose; it doesn't matter what order the objects are in, only that the ordering is the same for both lists. In Python 3, though, comparing objects of different types is disallowed in many circumstances -- for example, you can't compare strings to integers -- so if you will have objects of various types, best to explicitly use the object's ID.)
If you want to compare the objects in the list by value, on the other hand, first you need to define what "value" means for the objects. Then you will need some way to provide that as a key (and for Python 3, as a consistent type). One potential way that would work for a lot of arbitrary objects is to sort by their repr(). Of course, this could waste a lot of extra time and memory building repr() strings for large lists and so on.
sorted(a, key=repr) == sorted(b, key==repr)
If the objects are all your own types, you can define __lt__() on them so that the object knows how to compare itself to others. Then you can just sort them and not worry about the key= parameter. Of course you could also define __hash__() and use Counter, which will be faster.
If the comparison is to be performed in a testing context, use assertCountEqual(a, b) (py>=3.2) and assertItemsEqual(a, b) (2.7<=py<3.2).
Works on sequences of unhashable objects too.
If the list contains items that are not hashable (such as a list of objects) you might be able to use the Counter Class and the id() function such as:
from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
print("Lists a and b contain the same objects")
Let a,b lists
def ass_equal(a,b):
try:
map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
if len(a) == 0: # if a is empty, means that b has removed them all
return True
except:
return False # b failed to remove some items from a
No need to make them hashable or sort them.
I hope the below piece of code might work in your case :-
if ((len(a) == len(b)) and
(all(i in a for i in b))):
print 'True'
else:
print 'False'
This will ensure that all the elements in both the lists a & b are same, regardless of whether they are in same order or not.
For better understanding, refer to my answer in this question
You can write your own function to compare the lists.
Let's get two lists.
list_1=['John', 'Doe']
list_2=['Doe','Joe']
Firstly, we define an empty dictionary, count the list items and write in the dictionary.
def count_list(list_items):
empty_dict={}
for list_item in list_items:
list_item=list_item.strip()
if list_item not in empty_dict:
empty_dict[list_item]=1
else:
empty_dict[list_item]+=1
return empty_dict
After that, we'll compare both lists by using the following function.
def compare_list(list_1, list_2):
if count_list(list_1)==count_list(list_2):
return True
return False
compare_list(list_1,list_2)
from collections import defaultdict
def _list_eq(a: list, b: list) -> bool:
if len(a) != len(b):
return False
b_set = set(b)
a_map = defaultdict(lambda: 0)
b_map = defaultdict(lambda: 0)
for item1, item2 in zip(a, b):
if item1 not in b_set:
return False
a_map[item1] += 1
b_map[item2] += 1
return a_map == b_map
Sorting can be quite slow if the data is highly unordered (timsort is extra good when the items have some degree of ordering). Sorting both also requires fully iterating through both lists.
Rather than mutating a list, just allocate a set and do a left-->right membership check, keeping a count of how many of each item exist along the way:
If the two lists are not the same length you can short circuit and return False immediately.
If you hit any item in list a that isn't in list b you can return False
If you get through all items then you can compare the values of a_map and b_map to find out if they match.
This allows you to short-circuit in many cases long before you've iterated both lists.
plug in this:
def lists_equal(l1: list, l2: list) -> bool:
"""
import collections
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
ref:
- https://stackoverflow.com/questions/9623114/check-if-two-unordered-lists-are-equal
- https://stackoverflow.com/questions/7828867/how-to-efficiently-compare-two-unordered-lists-not-sets
"""
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
set_comp = set(l1) == set(l2) # removes duplicates, so returns true when not sometimes :(
multiset_comp = compare(l1, l2) # approximates multiset
return set_comp and multiset_comp #set_comp is gere in case the compare function doesn't work
In PyCharm, when I write:
return set([(sy + ady, sx + adx)])
it says "Function call can be replaced with set literal" so it replaces it with:
return {(sy + ady, sx + adx)}
Why is that? A set() in Python is not the same as a dictionary {}?
And if it wants to optimize this, why is this more effective?
Python sets and dictionaries can both be constructed using curly braces:
my_dict = {'a': 1, 'b': 2}
my_set = {1, 2, 3}
The interpreter (and human readers) can distinguish between them based on their contents. However it isn't possible to distinguish between an empty set and an empty dict, so this case you need to use set() for empty sets to disambiguate.
A very simple test suggests that the literal construction is faster (python3.5):
>>> timeit.timeit('a = set([1, 2, 3])')
0.5449375328607857
>>> timeit.timeit('a = {1, 2, 3}')
0.20525191631168127
This question covers some issues of performance of literal constructions over builtin functions, albeit for lists and dicts. The summary seems to be that literal constructions require less work from the interpreter.
It is an alternative syntax for set()
>>> a = {1, 2}
>>> b = set()
>>> b.add(1)
>>> b.add(2)
>>> b
set([1, 2])
>>> a
set([1, 2])
>>> a == b
True
>>> type(a) == type(b)
True
dict syntax is different. It consists of key-value pairs. For example:
my_obj = {1:None, 2:None}
Another example how set and {} are not interchangeable (as jonrsharpe mentioned):
In: f = 'FH'
In: set(f)
Out: {'F', 'H'}
In: {f}
Out: {'FH'}
set([iterable]) is the constructor to create a set from the optional iterable iterable. And {} is to create set / dict object literals. So what is created depends on how you use it.
In [414]: x = {}
In [415]: type(x)
Out[415]: dict
In [416]: x = {1}
In [417]: type(x)
Out[417]: set
In [418]: x = {1: "hello"}
In [419]: type(x)
Out[419]: dict
This question already has answers here:
Python: Determine if an unsorted list is contained in a 'list of lists', regardless of the order to the elements
(4 answers)
Closed 9 years ago.
I need to check if list with lists contains list with similar values to the specified list, values may be in different order but in case all values are same it should return true
a= ["1","2","3","4","5"]
b= ["2","3","6","4","7"]
e = (["1","3","2","4","5"],["2","3","6","4","7"])
CombinationFound = []
for i in e:
if i == a:
CombinationFound = True
break;
else:
CombinationFound = False
it should return true since ["1","2","3","4","5"] and ["1","3","2","4","5"] have same values
Python sets are a much better implementation for your particular problem.
Sets are mathematical objects that contain data, but have methods to determine unions, intersections, differences etc between two collections.
Using:
set(a) == set(b)
should give you the result you want. As long as by 'similar' you mean 'the same'.
Different way to compare without the for loop:
found = any(set(a)==set(l) for l in e)
Try to convert those lists into sets:
def isthesame(a,b):
return set(b) == set(a)
For example if you have:
a= ["1","2","3","4","5"]
b= ["2","3","6","4","7"]
This solution should work for lists that don't have duplicit items.
You need to convert the list you're trying to match against into something you can compare against without caring about the original order. If the number of appearances of an element of the list counts, use sorted lists. If it does not, use sets.
list_to_match = sorted(a)
combination_found = False
for i in e:
if sorted(i) == list_to_match:
combination_found = True
break
That version will distinguish lists with different numbers of repeated elements - that is, [0, 1, 1, 2] will not match [0, 1, 1, 1, 2]. If you don't care about that possibility, use set(a) and set(i) instead:
set_to_match = set(a)
combination_found = False
for i in e:
if set(i) == set_to_match:
combination_found = True
break
Or, for a more concise version, use the built-in any function with a generator expression:
list_to_match = sorted(a)
combination_found = any(sorted(i) == list_to_match for i in e)
def checker(list_of_lists, example):
for i in list_of_lists:
if tuple(sorted(i)) == tuple(sorted(example)):
return True
return False
I've got a dict where some of the values are not hashable. I need some way to compare two unordered groups of these to ensure they contain equal elements. I can't use lists because list equality takes the order into account but sets won't work because dicts aren't hashable. I had a look through the python docs, and the only thing that looks useful is a dict's view, which is hashable under some circumstances but in this case this doesn't help either as one of the values is an object which contains lists itself, meaning the dict's view won't be hashable either.
Is there a standard container for situations like this, or should I just use lists and loop through every element in both lists and ensure an equal element is somewhere in the other list?
When duplicate entries don't exist, the usual choices are:
If the elements are hashable: set(a) == set(b)
If the elements are orderable: sorted(a) == sorted(b)
If all you have is equality: len(a) == len(b) and all(x in b for x in a)
If you have duplicates and their multiplicity matters, the choices are:
If the elements are hashable: Counter(a) == Counter(b)
If the elements are orderable: sorted(a) == sorted(b)
If all you have is equality: len(a) == len(b) and all(a.count(x) == b.count(x) for x in a)
I think the simplest method is to use lists.
group_1 = my_dict_1.values()
group_2 = my_dict_2.values()
Your comparison won't be as simple as if order mattered, or if the values were hashable, but the following should work:
def contain_the_same(group_1, group_2):
for item in group_1:
if item not in group_2:
return False
else:
group_2.pop(group_2.index(item))
if len(group_2) != 0:
return False
return True
This should be able to handle unhashable objects just fine:
>>> contain_the_same([1,2,3], [1,2,3])
True
>>> contain_the_same([1,2,3], [1,2,3,4])
False
>>> contain_the_same([1,2,[3,2,1]], [1,2,[3,2,1]])
True
>>> contain_the_same([5,1,2,[3,2,1,[1]]], [1,[3,2,1,[1]],2,5])
True
A caveat: This will return false if there are duplicates in one list, but no the other. This would require some modification if you wanted to make that an allowable case.
Edit: Even easier:
sorted(my_dict_1.values()) == sorted(my_dict_1.values())
It even looks like this is twice as fast as my contain_the_same function:
>>> timeit("contain_the_same([5,1,2,[3,2,1,[1]]], [1,[3,2,1,[1]],2,5])",
"from __main__ import contain_the_same", number=10000)/10000
8.868489032757054e-06
>>>timeit("sorted([5,1,2,[3,2,1,[1]]]) == sorted([1,[3,2,1,[1]],2,5])",
number=10000)/10000
4.928951884845034e-06
Although it would not be as easy to extend to the case where duplicates are allowed.
a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]
a & b should be considered equal, because they have exactly the same elements, only in different order.
The thing is, my actual lists will consist of objects (my class instances), not integers.
O(n): The Counter() method is best (if your objects are hashable):
def compare(s, t):
return Counter(s) == Counter(t)
O(n log n): The sorted() method is next best (if your objects are orderable):
def compare(s, t):
return sorted(s) == sorted(t)
O(n * n): If the objects are neither hashable, nor orderable, you can use equality:
def compare(s, t):
t = list(t) # make a mutable copy
try:
for elem in s:
t.remove(elem)
except ValueError:
return False
return not t
You can sort both:
sorted(a) == sorted(b)
A counting sort could also be more efficient (but it requires the object to be hashable).
>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True
If you know the items are always hashable, you can use a Counter() which is O(n)
If you know the items are always sortable, you can use sorted() which is O(n log n)
In the general case you can't rely on being able to sort, or has the elements, so you need a fallback like this, which is unfortunately O(n^2)
len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)
If you have to do this in tests:
https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual
assertCountEqual(first, second, msg=None)
Test that sequence first contains the same elements as second, regardless of their order. When they don’t, an error message listing the differences between the sequences will be generated.
Duplicate elements are not ignored when comparing first and second. It verifies whether each element has the same count in both sequences. Equivalent to: assertEqual(Counter(list(first)), Counter(list(second))) but works with sequences of unhashable objects as well.
New in version 3.2.
or in 2.7:
https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual
Outside of tests I would recommend the Counter method.
The best way to do this is by sorting the lists and comparing them. (Using Counter won't work with objects that aren't hashable.) This is straightforward for integers:
sorted(a) == sorted(b)
It gets a little trickier with arbitrary objects. If you care about object identity, i.e., whether the same objects are in both lists, you can use the id() function as the sort key.
sorted(a, key=id) == sorted(b, key==id)
(In Python 2.x you don't actually need the key= parameter, because you can compare any object to any object. The ordering is arbitrary but stable, so it works fine for this purpose; it doesn't matter what order the objects are in, only that the ordering is the same for both lists. In Python 3, though, comparing objects of different types is disallowed in many circumstances -- for example, you can't compare strings to integers -- so if you will have objects of various types, best to explicitly use the object's ID.)
If you want to compare the objects in the list by value, on the other hand, first you need to define what "value" means for the objects. Then you will need some way to provide that as a key (and for Python 3, as a consistent type). One potential way that would work for a lot of arbitrary objects is to sort by their repr(). Of course, this could waste a lot of extra time and memory building repr() strings for large lists and so on.
sorted(a, key=repr) == sorted(b, key==repr)
If the objects are all your own types, you can define __lt__() on them so that the object knows how to compare itself to others. Then you can just sort them and not worry about the key= parameter. Of course you could also define __hash__() and use Counter, which will be faster.
If the comparison is to be performed in a testing context, use assertCountEqual(a, b) (py>=3.2) and assertItemsEqual(a, b) (2.7<=py<3.2).
Works on sequences of unhashable objects too.
If the list contains items that are not hashable (such as a list of objects) you might be able to use the Counter Class and the id() function such as:
from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
print("Lists a and b contain the same objects")
Let a,b lists
def ass_equal(a,b):
try:
map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
if len(a) == 0: # if a is empty, means that b has removed them all
return True
except:
return False # b failed to remove some items from a
No need to make them hashable or sort them.
I hope the below piece of code might work in your case :-
if ((len(a) == len(b)) and
(all(i in a for i in b))):
print 'True'
else:
print 'False'
This will ensure that all the elements in both the lists a & b are same, regardless of whether they are in same order or not.
For better understanding, refer to my answer in this question
You can write your own function to compare the lists.
Let's get two lists.
list_1=['John', 'Doe']
list_2=['Doe','Joe']
Firstly, we define an empty dictionary, count the list items and write in the dictionary.
def count_list(list_items):
empty_dict={}
for list_item in list_items:
list_item=list_item.strip()
if list_item not in empty_dict:
empty_dict[list_item]=1
else:
empty_dict[list_item]+=1
return empty_dict
After that, we'll compare both lists by using the following function.
def compare_list(list_1, list_2):
if count_list(list_1)==count_list(list_2):
return True
return False
compare_list(list_1,list_2)
from collections import defaultdict
def _list_eq(a: list, b: list) -> bool:
if len(a) != len(b):
return False
b_set = set(b)
a_map = defaultdict(lambda: 0)
b_map = defaultdict(lambda: 0)
for item1, item2 in zip(a, b):
if item1 not in b_set:
return False
a_map[item1] += 1
b_map[item2] += 1
return a_map == b_map
Sorting can be quite slow if the data is highly unordered (timsort is extra good when the items have some degree of ordering). Sorting both also requires fully iterating through both lists.
Rather than mutating a list, just allocate a set and do a left-->right membership check, keeping a count of how many of each item exist along the way:
If the two lists are not the same length you can short circuit and return False immediately.
If you hit any item in list a that isn't in list b you can return False
If you get through all items then you can compare the values of a_map and b_map to find out if they match.
This allows you to short-circuit in many cases long before you've iterated both lists.
plug in this:
def lists_equal(l1: list, l2: list) -> bool:
"""
import collections
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
ref:
- https://stackoverflow.com/questions/9623114/check-if-two-unordered-lists-are-equal
- https://stackoverflow.com/questions/7828867/how-to-efficiently-compare-two-unordered-lists-not-sets
"""
compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
set_comp = set(l1) == set(l2) # removes duplicates, so returns true when not sometimes :(
multiset_comp = compare(l1, l2) # approximates multiset
return set_comp and multiset_comp #set_comp is gere in case the compare function doesn't work