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?

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.


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

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.


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)

12

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

len(set(x)) == len(x)

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)

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.


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

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.


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

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


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.


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

-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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.