我有一个元组列表,我想删除元组,以便列表中只有一个元组具有给定的长度和总和。
这是一个不好的解释,例如:
[(0,1,2), (0,2,1), (0,0,1)]
删除(0,1,2)或(0,2,1)
我希望能够遍历列表并删除满足以下条件的任何元组:
len(tuple1) == len(tuple2) and sum(tuple1) == sum(tuple2)
但在列表中保留tuple1或tuple2。
我尝试过:
for t1 in list:
for t2 in list:
if len(t1) == len(t2) and sum(t1) == sum(t2):
list.remove(t1)
但是我很确定这会删除所有元组并且控制台崩溃。
答案 0 :(得分:3)
从本质上讲,这是一个“唯一性过滤器”,但是在我们指定函数f
的情况下,只有当该f(x)
第二次出现时,我们才会将该元素过滤掉。
假设f(x)
产生 hashable 值,我们可以实现这种唯一性过滤器,
def uniq(iterable, key=lambda x: x):
seen = set()
for item in iterable:
u = key(item)
if u not in seen:
yield item
seen.add(u)
然后我们可以将此过滤器用作:
result = list(uniq(data, lambda x: (len(x), sum(x))))
例如:
>>> list(uniq(data, lambda x: (len(x), sum(x))))
[(0, 1, 2), (0, 0, 1)]
在这里,我们将始终保留首次出现的“重复项”。
答案 1 :(得分:2)
让我提供稍微不同的解决方案。请注意,这不是用于一次性脚本的东西,而是用于真实项目的东西。因为您的[(0, 0, 1)]
实际上代表逻辑/物理的东西。
set(..)
删除重复项。我们怎么用呢?唯一要记住的是,哈希值和元素的相等性需要修改。
class Converted(object):
def __init__(self, tup):
self.tup = tup
self.transformed = len(tup), sum(tup)
def __eq__(self, other):
return self.transformed == other.transformed
def __hash__(self):
return hash(self.transformed)
inp = [(0,1,2), (0,2,1), (0,0,1)]
out = [x.tup for x in set(map(Converted, inp))]
print(out)
# [(0, 0, 1), (0, 1, 2)]
答案 2 :(得分:1)
仅创建一个满足您条件的新列表可能会更容易。
old_list = [(0,1,2), (0,2,1), (0,0,1)]
new_list = []
for old_t in old_list:
for new_t in new_list:
if len(old_t) == len(new_t) and sum(old_t) == sum(new_t):
break
else:
new_list.append(old_t)
# new_list == [(0, 1, 2), (0, 0, 1)]
答案 3 :(得分:1)
您还可以使用groupby
将元素按sum
和len
分组,并从每个组中提取1个元素以创建新列表:
from itertools import groupby
def _key(t):
return (len(t), sum(t))
data = [(0, 1, 2), (0, 2, 1), (0, 0, 1), (1, 0, 0), (0, 1, 0), (3, 0, 0, 0)]
result = []
for k, g in groupby(sorted(data, key=_key), key=_key):
result.append(next(g))
print(result)
# [(0, 0, 1), (0, 1, 2), (3, 0, 0, 0)]
答案 4 :(得分:1)
问题的复杂性主要来自以下事实:您有两个要实现的独立过滤器。对具有此类要求的数据进行过滤的一种好方法是使用groupby
。但是,在执行此操作之前,需要先进行排序。由于通常对一个键进行排序,因此需要进行两次排序才能分组:
from itertools import groupby
def lensumFilter(data):
return [next(g) for _, g in groupby(sorted(sorted(data, key = len), key = sum),
key = lambda x: (len(x), sum(x)))]
>>> print(lensumFilter( [(0, 1, 2), (0, 2, 1), (0, 0, 1)] )
[(0, 0, 1), (0, 2, 1)]
>>> print(lensumFilter( [(0, 1, 2), (0, 2, 1), (0, 0, 0, 3), (0, 0, 1)] )
[(0, 0, 1), (0, 2, 1), (0, 0, 0, 3)]
>>> print(lensumFilter( [(0, 1, 2), (0, 2, 2), (0, 4), (0, 0, 0, 5), (0, 0, 3)] )
[(0, 1, 2), (0, 4), (0, 2, 2), (0, 0, 0, 5)]
请注意,如果您更改排序方式,则会更改输出的外观。例如,我先对长度排序,然后求和,以便求和结果在总和分组中相对于总和的顺序是最小的(首先是最小的总和),然后相对于长度是我的顺序(最小的元素个数)。这就是(0, 1, 2)
在(0, 4)
之前但(0, 4)
在(0, 2, 2)
之前的原因。
答案 5 :(得分:1)
如果您想做一些简洁明了的Python语句,可以使用函数filter。 它将保留所有符合您要求的元素(在相同长度下,总和不相等):
tup_remove = (0,2,1)
list(filter(lambda current_tup: not (sum(tup_remove) == sum(current_tup) and len(tup_remove) == len(current_tup))
为了获得更好的可读性和可扩展性,我鼓励您使用一个函数:
def not_same_sum_len_tuple(tup_to_check, current_tuple):
"""Return True when not same sum AND same length"""
same_sum = sum(tup_to_check) == sum(current_tuple) # Check the sum
same_len = len(tup_remove) == len(current_tuple) # Check the length
return not (same_sum and same_len)
tup_remove = (0,2,1)
list(filter(lambda current_tup: not_same_sum_len_tuple(tup_remove, current_tup), tup_list))
答案 6 :(得分:0)
这是一个更简单的解决方案,但可能不起作用。只需使用dict
作为键,(len(t), sum(t))
作为值来制作tuple
。最后一个tuple
停留。
lst = [(0,1,2), (0,2,1), (0,0,1)]
d = {(len(t), sum(t)): t for t in lst}
list(d.values())
一行;
list({(len(t), sum(t)): t for t in lst}.values())
要使其表现出色,只需记住len
和sum
。
from functools import lru_cache
mlen, msum = (lru_cache(maxsize=None)(f) for f in (len, sum))
list({(mlen(t), msum(t)): t for t in lst}.values())