Предполагая, что вы хотите, чтобы это работало для произвольного числа последовательностей, прямой (но, вероятно, не самый эффективный - вероятно, объект others
может быть создан из последней итерации) способ решить эту проблему:
def deep_unique_set(*seqs):
for i, seq in enumerate(seqs):
others = set(x for seq_ in (seqs[:i] + seqs[i + 1:]) for x in seq_)
yield [x for x in seq if x not in others]
или чуть более быстрый, но менее эффективный по памяти и в остальном идентичный:
def deep_unique_preset(*seqs):
pile = list(x for seq in seqs for x in seq)
k = 0
for seq in seqs:
num = len(seq)
others = set(pile[:k] + pile[k + num:])
yield [x for x in seq if x not in others]
k += num
Тестирование с предоставленным входом:
s1 = ['a', 'b', 'c']
s2 = ['a', 'potato', 'd']
s3 = ['a', 'b', 'h']
print(list(deep_unique_set(s1, s2, s3)))
# [['c'], ['potato', 'd'], ['h']]
print(list(deep_unique_preset(s1, s2, s3)))
# [['c'], ['potato', 'd'], ['h']]
Обратите внимание, что если вход содержат дубликаты в одном из списков, они не удаляются, то есть:
s1 = ['a', 'b', 'c', 'c']
s2 = ['a', 'potato', 'd']
s3 = ['a', 'b', 'h']
print(list(deep_unique_set(s1, s2, s3)))
# [['c', 'c'], ['potato', 'd'], ['h']]
print(list(deep_unique_preset(s1, s2, s3)))
# [['c', 'c'], ['potato', 'd'], ['h']]
Если все дубликаты должны быть удалены, лучшим подходом является подсчет значений. Метод выбора для этого заключается в использовании collections.Counter
, как предложено в @ Kasramvd answer :
def deep_unique_counter(*seqs):
counts = collections.Counter(itertools.chain.from_iterable(seqs))
for seq in seqs:
yield [x for x in seq if counts[x] == 1]
s1 = ['a', 'b', 'c', 'c']
s2 = ['a', 'potato', 'd']
s3 = ['a', 'b', 'h']
print(list(deep_unique_counter(s1, s2, s3)))
# [[], ['potato', 'd'], ['h']]
В качестве альтернативы можно отслеживать повторы, например:
def deep_unique_repeat(*seqs):
seen = set()
repeated = set(x for seq in seqs for x in seq if x in seen or seen.add(x))
for seq in seqs:
yield [x for x in seq if x not in repeated]
, который будет вести себя так же, как и collections.Counter
подход:
s1 = ['a', 'b', 'c', 'c']
s2 = ['a', 'potato', 'd']
s3 = ['a', 'b', 'h']
print(list(deep_unique_repeat(s1, s2, s3)))
# [[], ['potato', 'd'], ['h']]
, но немного быстрее, так как ему не нужно отслеживать неиспользуемые подсчеты.
Другой, крайне неэффективный, использует list.count()
для подсчета вместо глобального счетчика:
def deep_unique_count(*seqs):
pile = list(x for seq in seqs for x in seq)
for seq in seqs:
yield [x for x in seq if pile.count(x) == 1]
Эти два последних подхода также предложены в @ AlainT. ответ .
Ниже приведены некоторые сроки:
n = 100
m = 100
s = tuple([random.randint(0, 10 * n * m) for _ in range(n)] for _ in range(m))
for func in funcs:
print(func.__name__)
%timeit list(func(*s))
print()
# deep_unique_set
# 10 loops, best of 3: 86.2 ms per loop
# deep_unique_preset
# 10 loops, best of 3: 57.3 ms per loop
# deep_unique_count
# 1 loop, best of 3: 1.76 s per loop
# deep_unique_repeat
# 1000 loops, best of 3: 1.87 ms per loop
# deep_unique_counter
# 100 loops, best of 3: 2.32 ms per loop