Проверка на уникальность, в которой 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 шт):
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
Реализовал это следующим образом:
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