Alfredo Gimenez
Alfredo Gimenez

Reputation: 2224

How to deeply compare nested types in Python

In python, it's easy to test whether two variables have the same top-level type:

In [1]: s1 = 'bob'
In [2]: s2 = 'tom'
In [3]: type(s1) == type(s2)
Out[3]: True

But in the case where types are nested, it's not so easy:

In [4]: strlist = ['bob', 'tom']
In [5]: intlist = [5, 6, 7]
In [6]: type(strlist) == type(intlist)
Out[6]: True

Is there a general way to "deeply" compare two variables such that:

deepcompare(['a', 'b'], [1, 2]) == False
deepcompare([42, 43], [1, 2]) == True

?

EDIT:

To define the question a bit more, let's say this includes both list length and heterogeneous list types:

deepcompare([1, 2, 3], [1, 2]) == False
deepcompare([1, 3], [2, 'b']) == False
deepcompare([1, 'a'], [2, 'b']) == True

Upvotes: 3

Views: 951

Answers (2)

jonrsharpe
jonrsharpe

Reputation: 122032

To expand on my comment, you could create what I've called a "type map" recursively:

def typemap(lst_or_obj):
    if not isinstance(lst_or_obj, list):
        return type(lst_or_obj)
    return [typemap(obj) for obj in lst_or_obj]

Then use this to get the types within your structures:

a = [1, 2, ['three', 4]]
b = [5, 6, ['seven', 8]]
c = [9, 10, [11, 'twelve']]

ta = typemap(a)
tb = typemap(b)
tc = typemap(c)

print(ta)
print(tb)
print(tc)

print(ta == tb)
print(ta == tc)

Output:

[<class 'int'>, <class 'int'>, [<class 'str'>, <class 'int'>]]
[<class 'int'>, <class 'int'>, [<class 'str'>, <class 'int'>]] 
[<class 'int'>, <class 'int'>, [<class 'int'>, <class 'str'>]]
True
False

Then your function is simply:

def deepcompare(a, b):
    return typemap(a) == typemap(b)

If you need to deal with things other than lists, you can trivially expand the isinstance check to (list, tuple), but you can quickly run into issues with things like str (recursively iterating over strings is a problem because a single character or empty string is an iterable of itself, so your program explodes) and dict (ordering issues, comparing keys and/or values, ...).

Upvotes: 2

Aaron Christiansen
Aaron Christiansen

Reputation: 11807

The way that I do this is by using this function:

def getDeepTypes(items):
    types = [type(x) for x in items]
    return (types[0] if all(x == types[0] for x in types) else None)

This uses various list comprehensions to get the deep type of a list. If they aren't all the same, None is returned.

>>> getDeepTypes([1, 2, 3])
int
>>> getDeepTypes(["foo", "bar"])
str
>>> print(getDeepTypes([1, "foo"]))
None

So you could do:

getDeepTypes(['a', 'b']) == getDeepTypes([1, 2]) # False
getDeepTypes([42, 43]) == getDeepTypes([1, 2]) # True

Upvotes: 1

Related Questions