Удалите аналогичный кортеж из словаря кортежей

#python #python-3.x #python-2.7

Вопрос:

У меня есть словарь кортежей, как показано на рисунке: mydict = {0: (12, 89), 1: (23, 78), 2: (34, 67), 3: (45, 56), 4: (56, 45), 5: (67, 34), 6: (78, 23), 7: (89, 12)}

Здесь последние четыре элемента (56, 45), (67, 34), (78, 23), (89, 12) являются дубликатами первых четырех элементов, но расположены в другом порядке, и я хочу его удалить.

Я использую приведенный ниже подход, но это приведет к удалению только в том случае, если кортежи одинаковы. Например: (12, 89) = (12, 89).

 values = mydict.values()
for x, y in mydict.items():
   for i in values:
         if i not in mydict.items():
             print("Result", x, y)
 

Каков эффективный способ получить желаемый результат ? Я хочу сравнить, может быть, например (12, 89) и (89, 12), и поскольку в нем есть те же элементы, я хочу удалить один из них.

Кто-нибудь может мне помочь с этим ?

Комментарии:

1. Вам нужно создать новый диктант, в котором будут отсортированы все значения. Это позволит выполнить сравнение‘==‘.

Ответ №1:

Ваши варианты зависят от того, заботитесь ли вы о порядке кортежей после удаления и упорядочен ли ваш входной словарь (python 3.6 ).

Никаких заказов

Решение 1 (Python 3.6 )

В случае, если вы не заботитесь о порядке и используете python 3.6 , вы можете использовать следующий трюк:

 filtered = {tuple(sorted(val)): key for key, val in mydict.items().__reversed__()}
restored = {val: key for key, val in filtered.items()}
 
 {0: (12, 89), 1: (23, 78), 2: (34, 67), 3: (45, 56)}
 

Идея построена на концепции, согласно которой python dict сортируется на 3.6 , это означает, что первые ключи будут вставлены позже в dict из-за __reversed__ порядка. В первой строке перевернуты ключ и значение, следовательно, любые повторяющиеся значения будут перезаписаны (именно поэтому мы делаем это в обратном порядке, поэтому последние элементы перезаписываются в более ранних элементах). Вторая строка возвращает ключ и значение обратно.

Важное замечание в приведенном выше решении заключается в том, что теперь кортежи отсортированы. Это означает , что если бы вы имели 0: (89, 12) , это стало бы 0: (12, 89) вместо этого.

Решение 2 (Любая версия)

Первый трюк действительно зависит от информации о том, что более высокие значения ключа могут быть устранены путем замены их более низкими значениями ключа. Чтобы обеспечить это условие, мы можем создать упорядоченную структуру, отсортировав их на основе отсортированного значения ( x[1] ) и их ключа ( x[0] ).

 ordered = sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__()
 

Что приводит к следующему порядку

 [(4, (56, 45)), (3, (45, 56)), (5, (67, 34)), (2, (34, 67)), (6, (78, 23)), (1, (23, 78)), (7, (89, 12)), (0, (12, 89))]
 

Затем повторно нанесите раствор 1:

 filtered = {tuple(sorted(val)): key for key, val in ordered}
restored = {val: key for key, val in filtered.items()}
 

Мы закончили.

Поддерживайте порядок

Решение 1 (Python 3.6 )

 filtered = {tuple(sorted(val)): key for key, val in mydict.items().__reversed__()}
restored = {val: mydict[val] for key, val in filtered.items()}
 

Аналогично первому решению без порядка, но теперь использует ключ, чтобы получить исходное значение из первого словаря. Поэтому значения будут прежними, другими словами 0: (89, 12) , останутся 0: (89, 12) .

Решение 2 (Любая версия)

Аналогично решению в неупорядоченном варианте, мы повторно используем исходный словарь для создания правильных индексов.

 ordered = sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__()
filtered = {tuple(sorted(val)): key for key, val in ordered}
restored = {val: mydict[val] for key, val in filtered.items()}
 

Примечания

Чтобы увидеть разницу между решениями, было бы желательно поменять порядок 0: (12, 89) на 0: (89, 12) .

Все вместе взятое:

 mydict = {0: (89, 12), 1: (23, 78), 2: (34, 67), 3: (45, 56), 4: (56, 45),
          5: (67, 34), 6: (78, 23), 7: (89, 12)}

if __name__ == '__main__':
    filtered = {tuple(sorted(val)): key for key, val in mydict.items().__reversed__()}
    restored = {val: key for key, val in filtered.items()}
    print(restored)

    restored = {val: mydict[val] for key, val in filtered.items()}
    print(restored)

    ordered = list(sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__())
    filtered = {tuple(sorted(val)): key for key, val in ordered}
    restored = {val: key for key, val in filtered.items()}
    print(restored)

    ordered = sorted(mydict.items(), key=lambda x: (sorted(x[1]), x[0])).__reversed__()
    filtered = {tuple(sorted(val)): key for key, val in ordered}
    restored = {val: mydict[val] for key, val in filtered.items()}
    print(restored)
 

И вывод с использованием python 3.9:

 {0: (12, 89), 1: (23, 78), 2: (34, 67), 3: (45, 56)}  # No order python 3.6 
{3: (45, 56), 2: (34, 67), 1: (23, 78), 0: (12, 89)}  # No order any version
{0: (89, 12), 1: (23, 78), 2: (34, 67), 3: (45, 56)}  # Order python 3.6 
{3: (45, 56), 2: (34, 67), 1: (23, 78), 0: (89, 12)}  # Order any version
 

Редактировать

Как указано Jasmijn , более правильным решением для случаев, когда порядок не имеет значения, было бы заменить tuple(sorted(val)) на frozenset(val) .

Комментарии:

1. Если порядок не важен frozenset , может быть более подходящим типом данных, чем сортированный tuple s.

2. Это действительно лучшее решение. Я вставил его в ответ, чтобы указать вариант. Спасибо!