Memeriksa apakah semua elemen dalam daftar unik

104

Apa cara terbaik (terbaik seperti cara konvensional) untuk memeriksa apakah semua elemen dalam daftar unik?

Pendekatan saya saat ini menggunakan Counteradalah:

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

Bisakah saya melakukan lebih baik?

pengguna225312
sumber

Jawaban:

164

Bukan yang paling efisien, tapi terus terang dan ringkas:

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

Mungkin tidak akan membuat banyak perbedaan untuk daftar pendek.

yan
sumber
Inilah yang saya lakukan juga. Mungkin tidak efisien untuk daftar besar.
tkerwin
Belum tentu, itu akan mengeksekusi isi kondisional jika daftar memiliki elemen berulang ("#melakukan sesuatu" dalam contoh).
yan
2
Solusi yang cukup adil. Saya menangani hampir <500 elemen, jadi ini harus melakukan apa yang saya inginkan.
pengguna225312
4
Bagi mereka khawatir tentang efisiensi dengan daftar panjang, ini adalah efisien untuk daftar panjang yang benar-benar unik (di mana semua elemen perlu memeriksa). Solusi keluar awal membutuhkan waktu lebih lama (kira-kira 2x lebih lama dalam pengujian saya) untuk daftar yang sebenarnya unik. Jadi ... jika Anda mengharapkan sebagian besar daftar Anda unik, gunakan solusi pemeriksaan panjang set sederhana ini. Jika Anda mengharapkan sebagian besar daftar Anda TIDAK unik, gunakan solusi keluar awal. Yang mana yang akan digunakan tergantung pada kasus penggunaan Anda.
Russ
Jawaban ini bagus. Namun, berhati-hatilah di sini: len(x) > len(set(x))adalah True bila elemen di dalam xTIDAK unik. Judul ini pertanyaan ini meminta persis sebaliknya: "Memeriksa apakah semua elemen dalam daftar yang unik"
WhyWhat
96

Berikut adalah dua baris yang juga akan melakukan keluar lebih awal:

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

Jika elemen x tidak dapat di-hash, maka Anda harus menggunakan daftar untuk 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
sumber
5
+1 bersih dan tidak mengulangi seluruh daftar jika tidak diperlukan.
Kos
@ paul-mcguire: Apakah Anda bersedia memberikan lisensi potongan kode ini di bawah lisensi yang kompatibel dengan Apache 2.0 (mis., Apache 2, 2/3-line BSD, MIT, X11, zlib). Saya ingin menggunakannya dalam proyek Apache 2.0 yang saya gunakan, dan karena persyaratan lisensi StackOverflow adalah fubar , saya meminta Anda sebagai penulis aslinya.
Ryan Parman
Saya telah mengeluarkan kode lain menggunakan lisensi MIT, jadi itu berfungsi untuk saya untuk cuplikan ini. Adakah hal khusus yang perlu saya lakukan?
PaulMcG
21

Solusi keluar awal bisa jadi

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

Namun untuk kasus-kasus kecil atau jika keluar lebih awal bukanlah kasus yang umum maka saya berharap len(x) != len(set(x))menjadi metode tercepat.

6502
sumber
Saya menerima jawaban lain karena saya tidak terlalu mencari pengoptimalan.
pengguna225312
2
Anda dapat mempersingkat ini dengan meletakkan baris berikut setelah s = set()...return not any(s.add(x) if x not in s else True for x in g)
Andrew Clark
Bisakah Anda menjelaskan mengapa Anda berharap len(x) != len(set(x))menjadi lebih cepat dari ini jika keluar lebih awal tidak umum? Bukankah kedua operasi itu O (len (x)) ? (di mana xdaftar aslinya)
Chris Redford
Oh, saya mengerti: metode Anda bukan O (len (x)) karena Anda memeriksa if x in sdi dalam O (len (x)) untuk loop.
Chris Redford
15

untuk kecepatan:

import numpy as np
x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
np.unique(x).size == len(x)
locojay
sumber
12

Bagaimana jika menambahkan semua entri ke satu set dan memeriksa panjangnya?

len(set(x)) == len(x)
Grzegorz Oledzki
sumber
1
Menjawab satu detik setelah yan, aduh. Pendek dan manis. Ada alasan mengapa tidak menggunakan solusi ini?
jasonleonhard
Tidak semua urutan (terutama generator) mendukung len().
PaulMcG
9

Alternatif untuk a set, Anda dapat menggunakan file dict.

len({}.fromkeys(x)) == len(x)
Tugrul Ates
sumber
9
Saya sama sekali tidak melihat keuntungan menggunakan dict di atas satu set. Tampaknya hal-hal yang tidak perlu mempersulit.
metasoarous
3

Pendekatan lain sepenuhnya, menggunakan sort dan groupby:

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

Ini membutuhkan semacam, tetapi keluar pada nilai berulang pertama.

PaulMcG
sumber
hashing lebih cepat daripada menyortir
IceArdor
Datang ke sini untuk memposting solusi yang sama menggunakan groupbydan menemukan jawaban ini. Menurut saya ini paling elegan, karena ini adalah ekspresi tunggal dan bekerja dengan alat bawaan tanpa memerlukan variabel tambahan atau pernyataan loop.
Lars Blumberg
1
Jika daftar Anda berisi objek arbitrer yang tidak dapat diurutkan, Anda dapat menggunakan id()fungsi untuk mengurutkannya karena ini merupakan prasyarat untuk groupby()bekerja:groupby(sorted(seq), key=id)
Lars Blumberg
3

Berikut adalah versi rekursif O (N 2 ) untuk bersenang-senang:

def is_unique(lst):
    if len(lst) > 1:
        return is_unique(s[1:]) and (s[0] not in s[1:])
    return True
Karol
sumber
2

Berikut adalah fungsi keluar awal rekursif:

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)    

Ini cukup cepat bagi saya tanpa menggunakan konversi aneh (lambat) sambil memiliki pendekatan gaya fungsional.

mhourdakis.dll
sumber
1
H in Tmelakukan pencarian linier, dan T = L[1:]menyalin bagian yang diiris dari daftar, jadi ini akan jauh lebih lambat daripada solusi lain yang telah disarankan pada daftar besar. Ini adalah O (N ^ 2) menurut saya, sementara sebagian besar lainnya adalah O (N) (set) atau O (N log N) (solusi berbasis pengurutan).
Blckknght
1

Bagaimana dengan ini

def is_unique(lst):
    if not lst:
        return True
    else:
        return Counter(lst).most_common(1)[0][1]==1
yilmazhuseyin.dll
sumber
0

Anda dapat menggunakan sintaks Yan (len (x)> len (set (x))), tetapi alih-alih set (x), tentukan fungsi:

 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

dan lakukan len (x)> len (f5 (x)). Ini akan cepat dan juga menjaga ketertiban.

Kode di sana diambil dari: http://www.peterbe.com/plog/uniqifiers-benchmark

canisrufus
sumber
fungsi f5 ini akan lebih lambat daripada menggunakan set yang dioptimalkan dengan lebih baik untuk kecepatan. Kode ini mulai rusak ketika daftar menjadi sangat besar karena mahal operasi "tambahkan". dengan list besar seperti x = range(1000000) + range(1000000), menjalankan set (x) lebih cepat dari f5 (x). Urutan bukanlah persyaratan dalam pertanyaan tetapi bahkan menjalankan terurut (set (x)) masih lebih cepat dari f5 (x)
OkezieE
0

Menggunakan pendekatan serupa dalam bingkai data Pandas untuk menguji apakah konten kolom berisi nilai unik:

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

Bagi saya, ini instan pada variabel int dalam kerangka tanggal yang berisi lebih dari satu juta baris.

pengguna1718097
sumber
0

semua jawaban di atas bagus tapi saya lebih suka menggunakan all_uniquecontoh python dari 30 detik

Anda perlu menggunakan set()daftar yang diberikan untuk menghapus duplikat, bandingkan panjangnya dengan panjang daftar.

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

ia mengembalikan Truejika semua nilai dalam daftar datar adalah unique, Falsesebaliknya

x = [1,2,3,4,5,6]
y = [1,2,2,3,4,5]
all_unique(x) # True
all_unique(y) # False
ArunPratap
sumber
-3

Untuk pemula:

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
DonChriss
sumber
Saya suka jawaban ini, hanya karena ini menunjukkan dengan baik kode apa yang tidak perlu Anda tulis saat menggunakan satu set. Saya tidak akan memberi label "untuk pemula", karena saya percaya para pemula harus belajar melakukannya dengan benar di depan; tetapi saya bertemu dengan beberapa pengembang tidak berpengalaman yang terbiasa menulis kode semacam itu dalam bahasa lain.
cessor