У меня есть список кортежей, и я хочу удалить кортежи, чтобы в списке был только один кортеж с заданной длиной и суммой.
Это плохое объяснение, например:
[(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)
но я уверен, что это удалит все кортежи, и консоль разбилась.
что, если кортеж [a, b, c, d, e, a '] - должен оставаться только a или a' или вы проверяете только соседние?






Наверное, проще просто составить новый список, соответствующий вашим условиям.
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)]
Python for имеет пункт else.
@NizamMohamed Я всегда забываю об этом, исправлено в редактировании, спасибо!
По сути, это «фильтр уникальности», но там, где мы указываем функцию f, и только если этот f(x) встречается во второй раз, мы отфильтровываем этот элемент.
Мы можем реализовать такой фильтр уникальности, учитывая, что f(x) выдает значения хэшируемый, с помощью:
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)]
Здесь мы всегда будем сохранять первое вхождение «дубликатов».
Вы также можете использовать 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)]
Мне это нравится, но не хватает длины.
@mVChr Я это пропустил. Фиксированный :)
Сложность вашей проблемы в основном связана с тем, что у вас есть два независимых фильтра, которые вы хотите реализовать. Хороший способ фильтрации данных с такими требованиями - использовать 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).
Позвольте предложить несколько иное решение. Обратите внимание, что я бы использовал это не для разового сценария, а для реального проекта. Потому что ваш [(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)]
Если вы хотите сделать что-то более лаконичное и питоническое, вы можете использовать функцию фильтр. Он сохранит все элементы, соответствующие вашим требованиям (здесь сумма не равна при одинаковой длине):
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))
Это более простое решение, но оно может быть неэффективным. Просто сделайте 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())
Также очень небезопасно использовать коллекцию повторять и модифицировать одновременно, так что это не лучший вариант.