Проверка уникальности всех элементов в списке

104

Как лучше всего (как обычно) проверить, все ли элементы в списке уникальны?

Мой текущий подход с использованием Counter:

>>> x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
>>> counter = Counter(x)
>>> for values in counter.itervalues():
        if values > 1: 
            # do something

Могу я сделать лучше?

user225312
источник

Ответы:

164

Не самый эффективный, но простой и лаконичный:

if len(x) > len(set(x)):
   pass # do something

Наверное, для коротких списков особой разницы не будет.

Ян
источник
Я тоже этим занимаюсь. Вероятно, неэффективно для больших списков.
tkerwin
Не обязательно, что это приведет к выполнению тела условного оператора, если в списке есть повторяющиеся элементы (в примере - «# сделать что-нибудь»).
янь
2
Достаточно честно, хорошее решение. Я обрабатываю едва <500 элементов, так что это должно делать то, что я хочу.
user225312
4
Для тех , кто беспокоится об эффективности с длинными списками, это является эффективным для длинных списков , которые на самом деле являются уникальными (где все элементы нужно проверять). Решения по раннему выходу требуют больше времени (примерно в 2 раза дольше в моих тестах) для действительно уникальных списков. Итак ... если вы ожидаете, что большинство ваших списков будут уникальными, используйте это простое решение для проверки длины набора. Если вы ожидаете, что большая часть ваших списков НЕ будет уникальной, используйте решение для раннего выхода. Какой из них использовать, зависит от вашего варианта использования.
Расс
Хороший ответ. Однако будьте осторожны: len(x) > len(set(x))имеет значение True, если элементы xНЕ уникальны. Название на этот вопрос спрашивает как раз наоборот: «Проверка , если все элементы в списке являются уникальными»
WhyWhat
96

Вот двухстрочный вариант, который также выполняет ранний выход:

>>> def allUnique(x):
...     seen = set()
...     return not any(i in seen or seen.add(i) for i in x)
...
>>> allUnique("ABCDEF")
True
>>> allUnique("ABACDEF")
False

Если элементы x не могут быть хешированы, вам придется прибегнуть к использованию списка для seen:

>>> def allUnique(x):
...     seen = list()
...     return not any(i in seen or seen.append(i) for i in x)
...
>>> allUnique([list("ABC"), list("DEF")])
True
>>> allUnique([list("ABC"), list("DEF"), list("ABC")])
False
PaulMcG
источник
5
+1 чистый и не перебирает весь список, если он не нужен.
Kos
@ paul-mcguire: согласны ли вы лицензировать этот фрагмент кода под лицензией, совместимой с Apache 2.0 (например, Apache 2, 2/3-строчная BSD, MIT, X11, zlib). Я хотел бы использовать его в проекте Apache 2.0, который я использую, и, поскольку условия лицензирования StackOverflow ошибочны , я прошу вас как первоначального автора.
Райан Парман
Я разместил другой код с использованием лицензии MIT, так что мне подходит этот фрагмент. Что-нибудь особенное мне нужно сделать?
PaulMcG
21

Решение для раннего выхода может быть

def unique_values(g):
    s = set()
    for x in g:
        if x in s: return False
        s.add(x)
    return True

однако для небольших случаев или если ранний выход не является обычным случаем, я ожидал бы, что len(x) != len(set(x))это самый быстрый метод.

6502
источник
Я принял другой ответ, поскольку особо не искал оптимизации.
user225312
2
Вы можете сократить это, поставив следующую строку после s = set()...return not any(s.add(x) if x not in s else True for x in g)
Эндрю Кларк
Не могли бы вы объяснить, почему вы ожидаете len(x) != len(set(x))быть быстрее, если ранний выход не является обычным явлением? Разве обе операции не O (len (x)) ? (где xнаходится исходный список)
Крис Редфорд
О, я вижу: ваш метод не O (len (x)), потому что вы проверяете if x in sвнутри цикла O (len (x)) for.
Крис Редфорд
15

по скорости:

import numpy as np
x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
np.unique(x).size == len(x)
локоджей
источник
12

Как насчет добавления всех записей в набор и проверки его длины?

len(set(x)) == len(x)
Гжегож Оледски
источник
1
Ответил через секунду после ян, ай. Коротко и мило. Есть ли причины, почему бы не использовать это решение?
jasonleonhard
Не все последовательности (особенно генераторы) поддерживаются len().
PaulMcG
9

Вместо a setвы можете использовать dict.

len({}.fromkeys(x)) == len(x)
Тугрул Атес
источник
9
Я не вижу абсолютно никаких преимуществ в использовании диктовки перед набором. Кажется, это излишне усложняет ситуацию.
metasoarous
3

Другой подход полностью, используя сортировку и группировку:

from itertools import groupby
is_unique = lambda seq: all(sum(1 for _ in x[1])==1 for x in groupby(sorted(seq)))

Требуется сортировка, но завершается работа по первому повторяющемуся значению.

PaulMcG
источник
хеширование быстрее сортировки
IceArdor
Пришел сюда, чтобы опубликовать то же решение, используя groupbyи нашел этот ответ. Я считаю это наиболее элегантным, поскольку это единственное выражение, которое работает со встроенными инструментами, не требуя дополнительных переменных или операторов цикла.
Lars Blumberg
1
Если в вашем списке есть произвольные объекты, которые нельзя сортировать, вы можете использовать id()функцию для их сортировки, поскольку это является предварительным условием для groupby()работы:groupby(sorted(seq), key=id)
Ларс Блумберг,
3

Вот рекурсивная версия O (N 2 ) для развлечения:

def is_unique(lst):
    if len(lst) > 1:
        return is_unique(s[1:]) and (s[0] not in s[1:])
    return True
Кароль
источник
2

Вот рекурсивная функция раннего выхода:

def distinct(L):
    if len(L) == 2:
        return L[0] != L[1]
    H = L[0]
    T = L[1:]
    if (H in T):
            return False
    else:
            return distinct(T)    

Для меня это достаточно быстро, без использования странных (медленных) преобразований при функциональном подходе.

Mhourdakis
источник
1
H in Tвыполняет линейный поиск и T = L[1:]копирует разрезанную часть списка, поэтому это будет намного медленнее, чем другие решения, предложенные в больших списках. Я думаю, это O (N ^ 2), в то время как большинство других - O (N) (наборы) или O (N log N) (решения на основе сортировки).
Blckknght
1

Как насчет этого

def is_unique(lst):
    if not lst:
        return True
    else:
        return Counter(lst).most_common(1)[0][1]==1
Йылмажусейн
источник
0

Вы можете использовать синтаксис Яна (len (x)> len (set (x))), но вместо set (x) определите функцию:

 def f5(seq, idfun=None): 
    # order preserving
    if idfun is None:
        def idfun(x): return x
    seen = {}
    result = []
    for item in seq:
        marker = idfun(item)
        # in old Python versions:
        # if seen.has_key(marker)
        # but in new ones:
        if marker in seen: continue
        seen[marker] = 1
        result.append(item)
    return result

и выполните len (x)> len (f5 (x)). Это будет быстро и одновременно с сохранением порядка.

Код взят из: http://www.peterbe.com/plog/uniqifiers-benchmark

Canisrufus
источник
эта функция f5 будет медленнее, чем использование набора, который лучше оптимизирован по скорости. Этот код начинает разрушаться, когда список становится действительно большим из-за дорогостоящей операции «добавления». с большими списками, например x = range(1000000) + range(1000000), set (x) быстрее, чем f5 (x). Порядок не является требованием в вопросе, но даже запуск sorted (set (x)) все равно быстрее, чем f5 (x)
OkezieE
0

Используя аналогичный подход в кадре данных Pandas, чтобы проверить, содержит ли содержимое столбца уникальные значения:

if tempDF['var1'].size == tempDF['var1'].unique().size:
    print("Unique")
else:
    print("Not unique")

Для меня это происходит мгновенно для переменной int в рамке даты, содержащей более миллиона строк.

user1718097
источник
0

все ответы выше хороши, но я предпочитаю использовать all_uniqueпример из 30 секунд python

вам нужно использовать set()в данном списке для удаления дубликатов, сравнить его длину с длиной списка.

def all_unique(lst):
  return len(lst) == len(set(lst))

он возвращается, Trueесли все значения в плоском списке равны unique, в Falseпротивном случае

x = [1,2,3,4,5,6]
y = [1,2,2,3,4,5]
all_unique(x) # True
all_unique(y) # False
АрунПратап
источник
-3

Для начинающих:

def AllDifferent(s):
    for i in range(len(s)):
        for i2 in range(len(s)):
            if i != i2:
                if s[i] == s[i2]:
                    return False
    return True
ДонЧрисс
источник
Мне нравится этот ответ просто потому, что он хорошо показывает, какой код вам не нужно писать при использовании набора. Я бы не назвал это «для новичков», поскольку считаю, что новички должны научиться делать это правильно заранее; но я встретил некоторых неопытных разработчиков, которые привыкли писать такой код на других языках.
cessor