Uji apakah semua nilai dalam daftar unik


90

Saya memiliki daftar kecil byte dan saya ingin menguji bahwa semuanya memiliki nilai yang berbeda. Misalnya, saya punya ini:

List<byte> theList = new List<byte> { 1,4,3,6,1 };

Apa cara terbaik untuk memeriksa apakah semua nilai berbeda atau tidak?


2
Karena ini adalah pertanyaan ruang kelas yang khas, saya akan menjawabnya dengan sebuah pertanyaan. Bagaimana Anda melakukannya jika sudah diurutkan?
ctrl-alt-delor

Jawaban:


168
bool isUnique = theList.Distinct().Count() == theList.Count();

Penasaran saja: apa sajakah kebutuhan ruang dan waktunya ini?
dtb

10
@dtb harus tentang O (N) . Tentu saja, mengingat ini adalah "daftar kecil", ini akan secepat kilat dengan hampir semua algoritme. IMO ini menang dalam hal keterbacaan dan keringkasan, dan karena kecepatan bukan masalah, itu membuatnya sempurna.
Tim S.

2
Ini adalah leff efisien daripada yang seharusnya
Jodrell

74

Berikut pendekatan lain yang lebih efisien daripada Enumerable.Distinct + Enumerable.Count(terlebih lagi jika urutannya bukan tipe koleksi). Ini menggunakan a HashSet<T>yang menghilangkan duplikat, sangat efisien dalam pencarian dan memiliki properti hitungan:

var distinctBytes = new HashSet<byte>(theList);
bool allDifferent = distinctBytes.Count == theList.Count;

atau pendekatan lain - yang lebih halus dan efisien:

var diffChecker = new HashSet<byte>();
bool allDifferent = theList.All(diffChecker.Add);

HashSet<T>.Addmengembalikan falsejika elemen tidak dapat ditambahkan karena sudah ada di HashSet. Enumerable.Allberhenti di "salah" pertama.


1
sangat sederhana dan jelas, mengapa saya tidak memikirkannya dulu :) Saya menggunakan satu baris ini dalam pengujian unit untuk mengonfirmasi 10 juta elemen yang dihasilkan oleh kode mengagumkan saya benar-benar unik Assert.IsTrue(samples.Add(AwesomeClass.GetUnique()));. Mereka dulu dan sekarang :) +1 untuk Anda Tim :)
grapkulec

1
saya telah mencoba jawaban Anda untuk pertanyaan ini tetapi tidak berhasil Pak: stackoverflow.com/questions/34941162/…
Learning-Overthinker-Confused

Seharusnya begini:bool allDifferent = theList.All(s => diffChecker.Add(s))
mike nelson

2
Tidak, tidak dibutuhkan. Dalam hal ini Anda dapat meneruskan delegasi secara langsung
Tim Schmelter

1
@ AndréReichelt - Saya baru saja membuka kode Anda dan skenario ketiga ( List.All(HashSet.Add)) tampaknya jauh lebih cepat daripada dua lainnya di hampir semua kasus
Kyle Delaney

6

Oke, berikut adalah metode paling efisien yang dapat saya pikirkan menggunakan .Net standar

using System;
using System.Collections.Generic;

public static class Extension
{
    public static bool HasDuplicate<T>(
        this IEnumerable<T> source,
        out T firstDuplicate)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var checkBuffer = new HashSet<T>();
        foreach (var t in source)
        {
            if (checkBuffer.Add(t))
            {
                continue;
            }

            firstDuplicate = t;
            return true;
        }

        firstDuplicate = default(T);
        return false;
    }
}

pada dasarnya, apa gunanya menghitung seluruh urutan dua kali jika yang ingin Anda lakukan hanyalah menemukan duplikat pertama.

Saya dapat mengoptimalkan ini lebih banyak dengan casing khusus, urutan elemen kosong dan tunggal, tetapi itu akan berkurang dari keterbacaan / pemeliharaan dengan keuntungan minimal.


Bagus menambahkan nilai duplikat keluar, cukup berguna untuk validasi
Pac0

Saya telah menguji 3 solusi di sini dan ini memang yang paling efisien di halaman ini. Beberapa kesalahan ketik di sana sekalipun (misalnya sequenceseharusnya source). Tetapi bekerja dengan baik setelah ini diperbaiki
mike nelson

@mikenelson, itu seharusnya lebih baik
Jodrell

2
Untuk keterbacaan, saya pikir itu harus if (!checkBuffer.Add(t)) { firstDuplicate = t; return true }dalam lingkaran.
tia

2

Logika yang mirip dengan Distinctmenggunakan GroupBy:

var isUnique = theList.GroupBy(i => i).Count() == theList.Count;

Ini berguna jika Anda ingin memeriksa keunikan sehubungan dengan properti theList.GroupBy(o => o.SomeProperty).Count() == theList.Count;sementara Distinct () tidak mengizinkannya.
Rev1.0

1

Anda juga dapat melakukannya: Gunakan Hashset

var uniqueIds = new HashSet<long>(originalList.Select(item => item.Id));

            if (uniqueIds.Count != originalList.Count)
            {
            }

0

Ada banyak solusi.

Dan tidak diragukan lagi lebih indah dengan penggunaan LINQ seperti "juergen d" dan "Tim Schmelter" disebutkan.

Tetapi, jika Anda menunjukkan "Kompleksitas" dan kecepatan, solusi terbaik adalah menerapkannya sendiri. Salah satu solusinya adalah membuat array dengan ukuran N (untuk byte 256). Dan loop array, dan pada setiap iterasi akan menguji indeks angka yang cocok jika nilainya 1 jika ya, itu berarti saya sudah menaikkan indeks array dan oleh karena itu array tidak berbeda jika tidak saya akan menaikkan sel array dan terus memeriksa .


2
Anda dapat menggunakan vektor bit dengan 256 bit = 32 byte = 8 bilangan bulat. Tetapi Big O = O (n) Anda masih akan sama dengan menggunakan Hashet yang diusulkan di jawaban lain.
BrokenGlass

Ini adalah O (n) jadi mungkin tercepat, (ujilah). Akankah pemeriksaan dihitung saat Anda pergi atau pada akhirnya menjadi yang tercepat? Saya menduga bahwa pada akhirnya akan memperbaiki kasus terburuk, tetapi seiring berjalannya waktu dapat meningkatkan kasus rata-rata dan terbaik). Jika tidak ada duplikat, ini akan menjadi kinerja kasus terburuk. Juga untuk tipe data yang lebih besar ini tidak akan bekerja dengan baik, untuk tipe 16bit Anda harus menggunakan jumlah 64k, baik 64k bit (8k byte), tetapi untuk yang lebih besar penggunaan memori akan mulai menjadi konyol. Namun saya suka jawaban ini untuk nilai 8bit.
ctrl-alt-delor

1
@TamusJRoyce jika Anda ingin menyimpan 4294967296 kemungkinan, Anda memerlukan 4GB bukan 42MB (atau 512MB Anda menggunakan bit masking)
tigrou

Tidak yakin apa yang saya pikirkan. "Alokasikan memori 42MB + untuk menampung semua kemungkinan 4294967296. Dan gunakan penghitung bucket sederhana. Atau bahkan gunakan bit masking xor dan periksa apakah ada bit yang diubah dari true ke false. 42MB + / 8 = 5MB + Pengeluarannya tampaknya terlalu besar dengan perangkat keras saat ini. Tapi suatu hari ini mungkin memiliki pahala. " bukanlah komentar yang relevan. Hashset adalah yang terbaik. Jika Anda berurusan dengan array yang sangat besar, Anda mengharapkan bagian memori yang sangat besar. Tetapi dalam kasus tepi yang aneh seperti itu, bidah dengan algoritma CRC akan lebih baik. Petakan ke polinomial. Jika dekat, evaluasi. Terima kasih @tigrou!
TamusJRoyce

0

Dan solusi lain, jika Anda ingin menemukan nilai duplikat.

var values = new [] { 9, 7, 2, 6, 7, 3, 8, 2 };

var sorted = values.ToList();
sorted.Sort();
for (var index = 1; index < sorted.Count; index++)
{
    var previous = sorted[index - 1];
    var current = sorted[index];
    if (current == previous)
        Console.WriteLine(string.Format("duplicated value: {0}", current));
}

Keluaran:

duplicated value: 2
duplicated value: 7

http://rextester.com/SIDG48202


0

Saya memeriksa apakah IEnumerable (aray, list, dll) unik seperti ini:

var isUnique = someObjectsEnum.GroupBy(o => o.SomeProperty).Max(g => g.Count()) == 1;
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.