Перевірка, чи всі елементи в списку унікальні


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

Чи можу я зробити краще?

Відповіді:


164

Не найефективніший, але прямий і стислий:

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

Напевно, не вдасться багато змінити для коротких списків.


Це я і роблю. Напевно, це не ефективно для великих списків.
tkerwin

Не обов'язково це буде виконувати тіло умовного, якщо в списку є елементи, що повторюються ("#do something" у прикладі).
Ян

2
Досить справедливе, хороше рішення. Я обробляю ледь <500 елементів, тому це повинно робити те, що я хочу.
користувач225312

4
Для тих , хто турбується про ефективність з довгими списками, це є ефективним для довгих списків , які на насправді є унікальними (де всі елементи потрібно перевіряти). Рішення для раннього виходу займають більше часу (приблизно в 2 рази довше в моїх тестах) для фактично унікальних списків. Отже ... якщо ви очікуєте, що більшість ваших списків буде унікальним, скористайтеся цим простим рішенням для перевірки довжини. Якщо ви очікуєте, що більшість ваших списків НЕ є унікальними, скористайтеся рішенням щодо раннього виходу. Який з них використовувати, залежить від вашого випадку використання.
Русс

Ця відповідь приємна. Однак будьте обережні тут: len(x) > len(set(x))це правда, коли елементи в 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

5
+1 чистим і не повторює весь список, якщо не потрібен.
Кос

@ paul-mcguire: Чи бажаєте ви ліцензувати цей фрагмент коду за ліцензією, сумісною з Apache 2.0 (наприклад, Apache 2, 2/3-рядковий BSD, MIT, X11, zlib). Я хотів би використовувати його в проекті Apache 2.0 , я використовую, і тому , що умови ліцензування StackOverflow є FUBAR , я питаю вас , як автор оригіналу.
Райан Парман

Я випустив інший код за допомогою ліцензії 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))що це найшвидший метод.


Я прийняв іншу відповідь, оскільки не особливо шукав оптимізації.
користувач225312

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 (х)) , тому що ви перевіряєте if x in sвсередині O (Len (х)) для циклу.
Кріс Редфорд

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, ви можете використовувати a dict.

len({}.fromkeys(x)) == len(x)

9
Я не бачу абсолютно жодної переваги від використання диктату над набором. Здається, непотрібно ускладнювати речі.
метасоарій

3

Інший підхід цілком, використовуючи сортування та групування:

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

Він вимагає сортування, але виходить на перше повторне значення.


хешування швидше, ніж сортування
IceArdor

Прийшов сюди, щоб опублікувати те саме рішення, використовуючи groupbyі знайшов цю відповідь. Я вважаю це найелегантнішим, оскільки це єдиний вираз та працює із вбудованими інструментами, не вимагаючи зайвих змінних чи контурних операторів.
Ларс Блюмберг

1
Якщо ваш список містить довільні об’єкти, які не можна id()сортувати , ви можете скористатися функцією для їх сортування, оскільки це є необхідною умовою для groupby()роботи:groupby(sorted(seq), key=id)
Lars Blumberg

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)    

Для мене це досить швидко, не використовуючи дивних (повільних) перетворень, використовуючи функціональний підхід.


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


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


ця функція f5 буде повільнішою, ніж використання набору, який краще оптимізований для швидкості. Цей код починає розбиватися, коли список стає дійсно великим через дорогу операцію "додавання". з великими списками на кшталт x = range(1000000) + range(1000000)запуску set (x) швидше, ніж f5 (x). Замовлення не є вимогою у питанні, але навіть сортування (сортування (x)) все ще швидше, ніж f5 (x)
OkezieE

0

Використовуючи аналогічний підхід у фреймі даних Pandas, щоб перевірити, чи вміст стовпця містить унікальні значення:

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

Для мене це миттєво на змінну int у рамці дат, що містить понад мільйон рядків.


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

Мені подобається ця відповідь, просто тому, що вона добре показує, який код вам не потрібно писати під час використання набору. Я не став би це позначити "для початківців", оскільки я вважаю, що початківці повинні навчитися робити це правильно вперед; але я зустрів деяких недосвідчених розробників, які звикли писати такий код іншими мовами.
цессор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.