Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic way of ignoring the last element when doing set difference

Let's say I have two set()s:

a = {('1', '2', '3', 'a'), ('1', '2', '4', 'a'), ('1', '2', '5', 'b')}
b = {('1', '2', '3', 'b'), ('1', '2', '4', 'b'), ('1', '2', '6', 'b')}

Now, what I want to do is to find the set difference b \ a but ignoring the last element from every tuple. So it's just like doing something like this:

a = {('1', '2', '3'), ('1', '2', '4'), ('1', '2', '5')}
b = {('1', '2', '3'), ('1', '2', '4'), ('1', '2', '6')}

In[1]: b - a
Out[1]: {('1', '2', '6')}

Expected output:

b \ a = {('1', '2', '6', 'b')}

Is there any obvious / pythonic way of achieving this without having to manually iterate over each set and check against each tuple[:3]?

like image 240
Grajdeanu Alex Avatar asked Dec 18 '19 13:12

Grajdeanu Alex


People also ask

How do you traverse the last element of a list in Python?

Get the last element of the list using the “length of list - 1” as an index and print the resultant last element of the list. Get the last element of the list using − 1(negative indexing) as the index and print the resultant last element of the list.

Does set in Python preserve order?

Unlike lists, ordinary sets do not preserve the order in which we insert the elements. This is because the elements in a set are usually not stored in the order in which they appear.

Does converting an object to a set maintain the object order?

1. Does converting an object to a set maintain the object's order? No. A set is not an ordered data structure, so order is not maintained.


1 Answers

Here's how you might write your own class to override a tuple's normal hashing behaviour:

a_data = [('1', '2', '3', 'a'), ('1', '2', '4', 'a'), ('1', '2', '5', 'b')]
b_data = [('1', '2', '3', 'b'), ('1', '2', '4', 'b'), ('1', '2', '6', 'b')]

class HashableIgnoresLastElement(tuple):
    def __eq__(self, other):
        return self[:-1] == other[:-1]

    def __hash__(self):
        return hash(self[:-1])

a = set(map(HashableIgnoresLastElement, a_data))
b = set(map(HashableIgnoresLastElement, b_data))

print(b - a)

with output

{('1', '2', '6', 'b')}

To modify the way sets of tuples behave, we have to modify the way tuples are hashed.

From here,

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.

So in order to make the hashing ignore the last element, we have to overload the dunder methods __eq__ and __hash__ appropriately. This doesn't end up being so hard because all we have to do is slice off the last element and then delegate to the appropriate methods of a normal tuple.

Further reading:

  • How to make an object properly hashable?
  • https://docs.python.org/3/reference/datamodel.html
like image 78
Izaak van Dongen Avatar answered Sep 19 '22 17:09

Izaak van Dongen