从列表列表中删除重复内容,同时不保留任何顺序



我正在用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)

使用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]]

如果输入很大,则使用集合而不是列表具有显著的性能优势。

>>> 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]]
>>> 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]]
>>> 

您可以保留一个集合来跟踪已看到的内容,并使用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]]
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]]

您可以使用toolz.unique来维护外部级别的排序。如果您无法访问第三方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))

实际上,以一般方式做到这一点非常棘手。

它基本上归结为两个基本问题:

  • 检查两个列表是否包含相同的元素
  • 删除包含相同元素的所有列表

我将分别解决这些问题。

检查两个列表是否包含相同的元素

我最好从这里参考雷蒙德·赫廷格斯的回答:

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]),或者它们将始终是唯一的,那么您也可以使用sets:

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

这样您就可以检查两个子列表是否包含相同的元素。如果您有不同长度的列表,则可能会进行优化,那么添加以下内容可能是值得的:

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

在每个函数的开头。

从列表中删除重复项

这也取决于对结果(非重复项是否应保持其相对顺序)和内容(再次,您可以对内容进行哈希处理或是否可以对其进行排序)的假设。

如果项目是可哈希的(或者可以转换为可哈希的内容),则可以使用set调用来删除重复项。如果您关心顺序,您仍然可以使用集合,但只能用于查找,例如 itertools 文档中的配方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]]

我期待投反对票,因为我的回答似乎有点跑题了。

首先,您只需要检查 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]]

我认为在这种情况下,您可以在此处使用一些领域知识。具体来说,你会得到[x, y]的对(我建议你把它做成一个元组(特别是一个 2 元组,也称为一对)而不是一个数组),并且[y,x]除非x=y你只得到一次。所以你可以写一个简单的函数:

def unique_factors(all_factors):
[ [a,b] for [a,b] in all_factors if a <= b ]
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() 来消除重复项。

我们可以按原样使用 List,因为列表的元素必须是可哈希的才能使用 set()。

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

这为我们提供了所有元素的集合,没有重复项。但它是一个集合,每个元素都是一个元组,但它们应该是列表。

我们可以使用列表推导将元组元素转换为列表。

最新更新