Удаление дублирующегося содержимого из списка списков, не сохраняя при этом никакого заказа

Я писал код в python, чтобы найти пары факторов для целого числа. Но создание пар привело к обратным парам. Я хочу исключить эти обратные пары с помощью простого метода без импорта каких-либо модулей. например.

[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20], [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]

выход должен быть:

[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]

Это то, что у меня есть до сих пор:

N = []
J = []
F = []
Z = []
S = []
num = input("Enter no. of elements in list")
print ('Enter numbers')
prod = 1
for i in range(int(num)):
    n = input("num :")
    N.append(int(n))
for x in N:
    prod = prod*x
print (prod)
k = input("Enter no. of splits:")
for o in range(1,prod+1):
    if prod%o == 0:
        J.append(o)
        F.append(o)
print (J)

Z = [[a, b] for a in J for b in F if a*b == prod]
print (Z)

Ответы

Ответ 1

>>> l = [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20], [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]
>>> new_l = []
>>> for e in l:
...     if e not in new_l and sorted(e) not in new_l:
...         new_l.append(e)
... 
>>> new_l
[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]
>>> 

Ответ 2

Использование set для удаления дубликатов.

Пример:

lst = [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20], [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]
lst = set([tuple(sorted(i)) for i in lst])      #Sort inner list and then use set
lst = list(map(list, lst))                      #Converting back to list
print(lst)

Выход:

[[8, 25], [4, 50], [1, 200], [10, 20], [2, 100], [5, 40]]

Ответ 3

Если вход большой, существует значительное преимущество в производительности за счет использования наборов вместо списков.

>>> unique = set(map(frozenset, pairs))
>>> unique
{frozenset({1, 200}),
 frozenset({10, 20}),
 frozenset({5, 40}),
 frozenset({2, 100}),
 frozenset({8, 25}),
 frozenset({4, 50})}

Внутренние наборы должны быть frozenset потому что обычные наборы изменяемы, а наборы могут содержать только неизменяемых детей.

Чтобы преобразовать обратно в список списков.

>>> list(map(list, unique))
[[200, 1], [10, 20], [40, 5], [2, 100], [8, 25], [50, 4]]

Наборы повторяемы, поэтому в зависимости от вашего использования этот шаг может и не понадобиться.

Здесь функция, которая выполняет оба действия и возвращает результат в виде вложенного списка.

def unique_pairs(pairs): 
    return list(map(list,set(map(frozenset, pairs))))

Обратите внимание, что преобразование списка в набор преобразует список, содержащий идентичные пары (например, [20,20]) в один набор элементов ({20}). Поэтому, если ваш вход может содержать одинаковые пары, вы можете сделать дополнительный заключительный шаг, чтобы развернуть одиночные пары обратно в пары.

def unique_pairs(pairs):
    return [(2*[*p])[:2] for p in set(map(frozenset,pairs))]

Это будет работать с бот-двойными парами и смешанными парами.

>>> pairs = [[10, 2], [2, 10], [10, 10], [2, 2]]
>>> unique_pairs(pairs)
[[10, 10], [2, 2], [10, 2]]

Ответ 4

Вы можете сохранить набор, чтобы следить за тем, что было видно, и использовать frozenset() для хэширования списков в frozenset() наборе:

seen = set()
result = []
for sublst in lst:
    curr = frozenset(sublst)
    if curr not in seen:
        seen.add(curr)
        result.append(sublst)

print(result)

Какие результаты:

[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]

Если позже вы захотите использовать библиотеки, вы можете использовать collections.OrderedDict():

lst = [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20], [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]

d = OrderedDict()
for sublist in lst:
    d.setdefault(frozenset(sublist), sublist)

print(list(d.values()))
# [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]

Ответ 5

myList = [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20], [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]
newList = myList.copy()
for i, j in newList:
    newList.remove([j,i])

print (newList)
#[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]

Ответ 6

Вы можете использовать toolz.unique для поддержания порядка на внешнем уровне. Если у вас нет доступа к 3 - ю участник toolz библиотеке, вы можете использовать unique_everseen рецепт из официальных документов.

L = [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20],
     [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]

from toolz import unique

res = list(unique(map(tuple, map(sorted, L))))

print(res)

[(1, 200), (2, 100), (4, 50),
 (5, 40), (8, 25), (10, 20)]

Требуется преобразование кортежа, так как unique использование хеширования; и кортежи хешируются, а списки - нет. Если это важно, у вас есть список списков, вы можете применить дополнительное преобразование:

res = list(map(list, unique(map(tuple, map(sorted, L)))))

На этом этапе это не особенно читаемо, поэтому я предлагаю вам разделить на несколько шагов:

sorter = map(sorted, L)
uniquify = unique(map(tuple, sorter))
res = list(map(list, uniquify))

Ответ 7

Я ожидаю downvote, потому что мой ответ кажется неприемлемым.

Во-первых, вам нужно проверить значение до int ((prod +1) ** 0.5) +1, который не обеспечивает дубликатов.

N = []
J = []
F = []
Z = []
S = []
num = input("Enter no. of elements in list: ")
print ('Enter numbers')
prod = 1
for i in range(int(num)):
    n = input("num :")
    N.append(int(n))
for x in N:
    prod = prod*x
print (prod)
k = input("Enter no. of splits:")
for o in range(1,int(prod**0.5)+1):
    if prod%o == 0:
        Z.append([o,prod//o])
print (Z)

Результат:

Enter no. of elements in list: 1
Enter numbers
num :200
Enter no. of splits:0
[1, 2, 4, 5, 8, 10]
[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]

Ответ 8

Я думаю, что в этом случае вы можете использовать некоторые знания домена. В частности, вы получите пары [x, y] (я рекомендую вам сделать этот кортеж (в частности, 2-кортеж, иначе известный как пара), а не массив) и [y,x] за исключением случаев, когда x=y где вы получаете его только один раз. Таким образом, вы можете написать простую функцию:

def unique_factors(all_factors):
  [ [a,b] for [a,b] in all_factors if a <= b ]

Ответ 9

На самом деле довольно сложно получить это право в общем виде.

Это, по сути, сводится к двум основным проблемам:

  • Проверка того, содержат ли два списка одни и те же элементы
  • Удалите все списки, содержащие одни и те же элементы

Я займусь этим отдельно.

Проверьте, содержат ли два списка одни и те же элементы

Я лучше обратиться к Raymond Hettingers ответ от сюда:

O (n): лучше всего работает метод Counter() (если ваши объекты хешируются):

from collections import Counter
def compare(s, t):
    return Counter(s) == Counter(t)

O (n log n): метод sorted() лучше подходит (если ваши объекты упорядочиваются):

def compare(s, t):
    return sorted(s) == sorted(t)

O (n * n): Если объекты не являются ни хешируемыми, ни упорядочиваемыми, вы можете использовать равенство:

def compare(s, t):
    t = list(t)   # make a mutable copy
    try:
        for elem in s:
            t.remove(elem)
    except ValueError:
        return False
    return not t

В вашем случае вы не хотите импорта, чтобы вы могли заменить collections.Counter помощью:

def count(it):
    d = {}
    for item in it:
        try:
            d[item] += 1
        except KeyError:
            d[item] = 1
    return d

На всякий случай, если элементы хешируются и вас не интересует количество элементов (например, [1,1,2] следует интерпретировать как равное [1,2,2]), или они всегда будут уникальными, тогда вы также можете использовать set s:

def compare(s, t):
    return set(s) == set(t)

Таким образом, вы можете проверить, содержат ли два подсписчика одни и те же элементы. Возможны оптимизации, если у вас могут быть списки разной длины, тогда было бы целесообразно добавить:

if len(s) != len(t):
    return False

В начале каждой из этих функций.

Удаление дубликатов из списка

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

Если элементы хешируются (или могут быть преобразованы в нечто хешируемое), вы можете использовать set вызов для удаления дубликатов. Если вы заботитесь о заказе, вы все равно можете использовать набор, но только для поиска, например, рецепт из документации unique_everseen:

from itertools import filterfalse

def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in filterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

Вы не указали импорт, но, к счастью, нам не нужен key is None часть (см. Ниже), поэтому вы можете просто использовать:

def unique_everseen(iterable, key):
    seen = set()
    seen_add = seen.add
    for element in iterable:
        k = key(element)
        if k not in seen:
            seen_add(k)
            yield element

Обратите внимание, что в подходах к сопоставлению внутренних списков используются наборы, словари и списки, которые не сотрясаются. Но все они могут быть преобразованы в хешируемые коллекции, такие как фризонсет или кортежи:

# for sets
frozenset(s)

# for dictionaries
frozenset(d.items())

# for lists
tuple(l)

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

В основном вы можете использовать unique_everseen следующим образом:

list(unique_everseen(your_list, key=lambda sublist: frozenset(count(sublist).items())))
# Or with collections.Counter instead of count

Или, если вам не нужны дубликаты (или не будет дубликатов) внутри ваших подсписок:

list(unique_everseen(your_list, key=frozenset))

Или, если они не хешируются, а могут быть заказаны:

list(unique_everseen(your_list, key=lambda sublist: tuple(sorted(sublist))))

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

def compare(s, t):
    t = list(t)   # make a mutable copy
    try:
        for elem in s:
            t.remove(elem)
    except ValueError:
        return False
    return not t

def unique_everseen_slow(iterable):
    seen = []
    for element in iterable:
        for already_seen_item in seen:
            if compare(element, already_seen_item):
                break  # We found a match, so stop looking
        else:
            seen.append(element)
            yield element

list(unique_everseen_slow(your_list))

Предложение else принадлежит циклу for и вводится только тогда, когда не было break. Вы могли бы вместо этого проверить, чтобы any избежал этого for - else:

def unique_everseen_slow(iterable):
    seen = []
    for element in iterable:
        if not any(compare(element, seen_element) for seen_element in seen):
            seen.append(element)
            yield element

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

Однако в вашем случае вы даже можете избежать создания повторяющихся факторов, просто проверив, что факторы сортируются (и если не останавливаются):

def factors(number):
    for candidate in range(1, number + 1):
        if number % candidate == 0:
            other_factor = number // candidate
            if candidate > other_factor:
                return
            yield [candidate, other_factor]

Например:

>>> list(factors(200))
[[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20]]

Ответ 10

lst = [[1, 200], [2, 100], [4, 50], [5, 40], [8, 25], [10, 20], [20, 10], [25, 8], [40, 5], [50, 4], [100, 2], [200, 1]]

[list(i) for i in set([tuple(sorted(i)) for i in l]))]

Ответ:

[(8, 25), (4, 50), (1, 200), (10, 20), (2, 100), (5, 40)]

Объяснение:

Сначала нам нужно отсортировать каждый список, чтобы мы могли сделать дубликат списка похожим. Затем нам нужно преобразовать каждый список в кортеж, чтобы мы могли использовать set() для устранения дубликатов.

Мы можем использовать Use List как есть, поскольку элементы списка должны быть хешируемыми для использования set().

set([tuple(sorted(i)) for i in l]) 

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

мы можем использовать представление списка, чтобы преобразовать элементы кортежа в списки.