удалить дубликаты (кортеж кортежей) - PullRequest
0 голосов
/ 02 октября 2018

входные данные:

input_tuple = (
            (1, 'name1', 'Noah'),
            (1, 'name2', 'Liam'),

            (2, 'name3', 'Mason'),

            (3, 'name4', 'Mason'),

            (4, 'name5', 'Noah'),
            (4, 'name6', 'Liam'),

            (5, 'name7', 'Elijah'),
            (5, 'name8', 'Noah'),
            (5, 'name9', 'Liam')
          )

, преобразованные в dict (ключ, значение):

input_tuple = {
         1: [['name1', 'Noah'], ['name2', 'Liam']],
         2: [['name3', 'Mason']],
         3: [['name4', 'Mason']],
         4: [['name5', 'Noah'], ['name6', 'Liam']],
         5: [['name7', 'Elijah'], ['name8', 'Noah'], 
             ['name9', 'Liam']]
         }

сделал еще один фильтр только для понимания модели данных:

    dict =   
    {
    1: ['Noah', 'Liam'],
    2: ['Mason'],
    3: ['Mason'],
    4: ['Noah', 'Liam'],
    5: ['Elijah', 'Noah', 'Liam']
    }

Теперь я хочу удалить дубликаты, а затем вернуться к кортежу, как показано ниже: условия соответствия дубликатов: 1) устранить дубликаты, если значение len (значение)> 1 2) должно точно совпадать, а не частично.

Примечание:значения ключей 2 и 3 не являются дубликатами, потому что len (значение) не равно -gt 1 значение ключа 4 утрачено, поскольку его точная копия, поскольку мы выполняем точное сопоставление, следовательно, в ключе 5 значение ['Noah', Liam] не будет идти.

 output_tuple = 
      (
        (1, 'name1', 'Noah'),
        (1, 'name2', 'Liam'),

        (2, 'name3', 'Mason'),

        (3, 'name4', 'Mason'),

        (5, 'name7', 'Elijah'),
        (5, 'name8', 'Noah'),
        (5, 'name9', 'Liam')
      )

код, который я пробовал:

from functools import reduce
from collections import defaultdict

input_tuple_dictionary = defaultdict(list)
for (key, *value) in input_tuple:
    input_tuple_dictionary[key].append(value[1])

input_tuple_dictionary
for index in range(len(input_tuple_dictionary)-1):
    for key, value in input_tuple_dictionary.items():
        if len(value) > 1:
            if value == value[index+1]:
                print(key)

Ответы [ 4 ]

0 голосов
/ 02 октября 2018

Вот одно решение, использующее defaultdict из set объектов и toolz.unique.toolz.unique эквивалентно рецепту itertools unique_everseen , доступному в документации.

Идея состоит в том, чтобы найти ключи с одиночными значениями, а также ключи, которые не имеют повторяющихся значений. объединение из этих двух категорий составляет ваш результат.

from collections import defaultdict
from toolz import unique

dd = defaultdict(set)

for k, _, v in input_tuple:
    dd[k].add(v)

lones = {k for k, v in dd.items() if len(v) == 1}
uniques = set(unique(dd, key=lambda x: frozenset(dd[x])))

res = tuple(i for i in input_tuple if i[0] in lones | uniques)

Результат:

print(res)

((1, 'name1', 'Noah'),
 (1, 'name2', 'Liam'),
 (2, 'name3', 'Mason'),
 (3, 'name4', 'Mason'),
 (5, 'name7', 'Elijah'),
 (5, 'name8', 'Noah'),
 (5, 'name9', 'Liam'))
0 голосов
/ 02 октября 2018

Одним из распространенных решений для пропуска дубликатов является сохранение набора, который содержит все элементы, которые вы уже видели.Если объект был замечен ранее, вы не добавляете его к результату.

Хитрость заключается в том, что объект, который вы пытаетесь удалить, является объединением нескольких объектов, которые находятся в разных кортежах.в вашей коллекции.Использование groupby - эффективный способ собрать эти объекты в один удобный пакет.

from itertools import groupby

input_tuple = (
    (1, 'name1', 'Noah'),
    (1, 'name2', 'Liam'),

    (2, 'name3', 'Mason'),

    (3, 'name4', 'Mason'),

    (4, 'name5', 'Noah'),
    (4, 'name6', 'Liam'),

    (5, 'name7', 'Elijah'),
    (5, 'name8', 'Noah'),
    (5, 'name9', 'Liam')
  )

seen = set()
result = []
for _, group in groupby(input_tuple, key=lambda t: t[0]):
    #convert from iterator to list, since we want to iterate this more than once
    group = list(group)
    #extract just the names from each tuple.
    names = tuple(t[2] for t in group)
    #check for duplicates, but only for name groups with more than one element.
    if len(names) == 1 or names not in seen:
        result.extend(group)
    seen.add(names)

print(result)

Результат:

[(1, 'name1', 'Noah'), (1, 'name2', 'Liam'), (2, 'name3', 'Mason'), (3, 'name4', 'Mason'), (5, 'name7', 'Elijah'), (5, 'name8', 'Noah'), (5, 'name9', 'Liam')]
0 голосов
/ 02 октября 2018
from collections import defaultdict

dct = defaultdict(list) 

for k,n_id,name in input_tuple:
    dct[k].append(name)

#print(dct)

seen = set()
ignore_id_set = set()

for _id, _namelst in dct.items():
    if len(_namelst) > 1:
        k = tuple(sorted(_namelst)) # note 1 

        if k not in seen:
            seen.add(k)
        else:
            ignore_id_set.add(_id) # duplicate

#print(seen)

# del dct,seen # dct,seen are now eligible for garbage collection

output = tuple(item for item in input_tuple if item[0] not in ignore_id_set)
print(output)


'''
note 1:
    important to sort **if** situations like this can be possible
     (1, 'name1', 'Noah'),
     (1, 'name2', 'Liam'),

     (4, 'name6', 'Liam'),
     (4, 'name5', 'Noah'),

     because when we will create dct it will result in 

     1 : [Noah,Liam]
     4 : [Liam,Noah]

     since we want to treat them as duplicates we need to sort before creating their hash( via tuple)

**else** no need to do sort

'''
0 голосов
/ 02 октября 2018
# Using the dict format of yours
data = [set(dict[x]) for x in range(1, len(dict) + 1)]
input_tuple = dict
seen = []
output_tuple = []
for i in range(len(data)):
    if (data[i] not in seen) or len(data[i]) == 1:
        for j in range(len(input_data)):
            if input_data[j][0] == i + 1:
                output_tuple.append(input_data[j])
    seen.append(data[i])
output_tuple = tuple(output_tuple)

Если вы не поняли, пожалуйста, спросите

Удачи

...