Python从列表中删除满足给定条件的元组

时间:2018-11-08 21:08:55

标签: python tuples

我有一个元组列表,我想删除元组,以便列表中只有一个元组具有给定的长度和总和。

这是一个不好的解释,例如:

[(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) 

但是我很确定这会删除所有元组并且控制台崩溃。

7 个答案:

答案 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将元素按sumlen分组,并从每个组中提取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())

要使其表现出色,只需记住lensum

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())