Проверка на уникальность, в которой False != 0

Требуется проверить JSON на уникальность. Условия проверки предполагают, что коллекция [false, 0] уникальна, то есть логический тип не равен числу.

Как сделать такую проверку на Python, если в нём логический тип является подклассом int и равен числам 1/0?

Примеры уникальных коллекций:

[1, True]
[[1], [True]]
[[[1], "foo"], [[True], "foo"]]
[{"foo": {"bar": [1]}}, {"foo": {"bar": [True]}})]

Прямое сравнение возвращает True:

{"foo": {"bar": [1]}} == {"foo": {"bar": [True]}})  # True

Уточню, что меня в основном интересует алгоритм разворачивания, который можно применить к последним конструкциям. Функция для сравнения 1 с True есть.

Я думаю, что задачу можно свести к BFS, но всё осложняется тем, что JSON одновременно содержит и словари, и списки. Хотелось бы найти универсальный и, желательно, нерекурсивный алгоритм обхода.


Ответы (2 шт):

Автор решения: pagislav
a = 0
b = False
print(a == b) # True
print(a == b and type(a) == type(b)) # False

Можно значения и типы сравнивать одновременно

def isUnique(a) -> bool:
    items = set([])

    for item in a:
        items.add(str([item]))

    return items.__len__() == a.__len__()

print(isUnique([1, True])) #True
print(isUnique([[1], [True]])) #True
print(isUnique([[[1], "foo"], [[True], "foo"]])) #True
print(isUnique([1,1])) # False
print(isUnique([[[True], "foo"], [["True"], "foo"]])) # True
print(isUnique([[[True], "foo"], [[True], "foo"]])) #False
→ Ссылка
Автор решения: nomnoms12

Реализовал это следующим образом:

def unbool(element, true=object(), false=object()):
    """
    A hack to make True and 1 and False and 0 unique for ``uniq``.
    """

    if element is True:
        return true
    elif element is False:
        return false
    return element


def unbool_nested(element):
    """
    Iterative implementation of breadth-first search for nested unbooling
    """

    if not isinstance(element, (list, dict)):
        return unbool(element)

    result = [] if isinstance(element, list) else {}
    stack = [(element, result)]
    while stack:
        element, new = stack.pop()
        items = enumerate(element) if isinstance(element, list) else element.items()
        for key, item in items:
            if not isinstance(item, (list, dict)):
                x = unbool(item)
            else:
                sub = [] if isinstance(item, list) else {}
                x = sub
                stack.append((item, sub))

            if isinstance(new, list):
                new.append(x)
            else:
                new[key] = x
    return result


def uniq(container):
    """
    Check if all of a container's elements are unique.

    Successively tries first to rely that the elements are hashable. If not, it goes to brute force.
    """

    try:
        return len(set(unbool(i) for i in container)) == len(container)
    except TypeError:
        seen = []
        for element in container:
            element = unbool_nested(element)
            if element in seen:
                return False
            seen.append(element)
    return True
→ Ссылка