Bagaimana cara memeriksa apakah semua item daftar memiliki nilai yang sama dan mengembalikannya, atau mengembalikan "nilai lain" jika tidak?


122

Jika semua item dalam daftar memiliki nilai yang sama, maka saya perlu menggunakan nilai itu, jika tidak, saya perlu menggunakan "otherValue". Saya tidak dapat memikirkan cara yang sederhana dan jelas untuk melakukan ini.

Lihat juga Neat way to write loop yang memiliki logika khusus untuk item pertama dalam sebuah koleksi.


Untuk perhatian Anda yang agak nakal, saya akan memilih jawaban Ani stackoverflow.com/questions/4390232/…
Binary Worrier

5
Apa yang Anda inginkan terjadi jika tidak ada nilai pertama karena daftarnya kosong? Dalam hal ini benar bahwa "semua item dalam daftar memiliki nilai yang sama" - jika Anda tidak mempercayai saya, temukan saya yang tidak memiliki nilai! Anda tidak menentukan apa yang harus dilakukan dalam situasi ini. Haruskah ini memunculkan pengecualian, mengembalikan nilai "lainnya", atau apa?
Eric Lippert

@Eric, maaf ketika daftar kosong itu harus mengembalikan nilai "lainnya"
Ian Ringrose

Jawaban:


153
var val = yyy.First().Value;
return yyy.All(x=>x.Value == val) ? val : otherValue; 

Cara terbersih yang bisa saya pikirkan. Anda dapat membuatnya menjadi satu baris dengan memasukkan val, tetapi First () akan dievaluasi sebanyak n kali, menggandakan waktu eksekusi.

Untuk memasukkan perilaku "set kosong" yang ditentukan dalam komentar, Anda cukup menambahkan satu baris lagi sebelum dua baris di atas:

if(yyy == null || !yyy.Any()) return otherValue;

1
+1, akankah menggunakan .Anymemungkinkan pencacahan untuk berhenti lebih awal dalam kasus di mana ada nilai yang berbeda?
Jeff Ogata

12
@adrift: Allakan berhenti segera setelah menyentuh elemen xdari urutannya x.Value != val. Demikian pula, Any(x => x.Value != val)akan berhenti segera setelah menyentuh elemen xdari urutan yang untuknya x.Value != val. Artinya, baik Alldan Anypameran "hubungan arus pendek" analog dengan &&dan ||(yang efektif apa Alldan Anyyang).
jason

@Jason: persis. Semua (kondisi) efektif! Semua (! Kondisi), dan evaluasi salah satunya akan dihentikan segera setelah jawabannya diketahui.
KeithS

4
Microoptimisation:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
Caltor

101

Tes cepat yang bagus untuk semua yang setara:

collection.Distinct().Count() == 1

1
Ini tidak akan bekerja dengan sembarang Class, meskipun harus bekerja dengan struct. Bagus untuk daftar primitif.
Andrew Backer

2
+1 jauh lebih bersih daripada IMO solusi KeithS. Anda mungkin ingin menggunakan collection.Distinct().Count() <= 1 jika Anda ingin mengizinkan koleksi kosong.
3dGrabber

4
Hati-hati, .Distinct()tidak selalu bekerja seperti yang diharapkan - terutama ketika Anda bekerja dengan objek, lihat pertanyaan ini . Dalam kasus tersebut, Anda perlu mengimplementasikan antarmuka IEquatable.
Matt

16
Lebih bersih, ya, tapi kurang berkinerja dalam kasus rata-rata; Distinct () dijamin melintasi setiap elemen dalam koleksi satu kali, dan dalam kasus terburuk setiap elemen berbeda, Count () akan melintasi daftar lengkap dua kali. Distinct () juga membuat HashSet sehingga perilakunya bisa linier dan bukan NlogN atau lebih buruk, dan itu akan meningkatkan penggunaan memori. All () membuat satu operan penuh dalam kasus terburuk semua elemen dianggap sama, dan tidak membuat koleksi baru.
KeithS

1
@KeithS Seperti yang saya harap Anda sadari sekarang, Distincttidak akan melintasi koleksi sama sekali, dan Countakan melakukan satu perjalanan melalui Distinctiterator.
NetMage

22

Meskipun Anda pasti dapat membuat perangkat seperti itu dari operator urutan yang ada, saya dalam hal ini cenderung menulis yang ini sebagai operator urutan khusus. Sesuatu seperti:

// Returns "other" if the list is empty.
// Returns "other" if the list is non-empty and there are two different elements.
// Returns the element of the list if it is non-empty and all elements are the same.
public static int Unanimous(this IEnumerable<int> sequence, int other)
{
    int? first = null;
    foreach(var item in sequence)
    {
        if (first == null)
            first = item;
        else if (first.Value != item)
            return other;
    }
    return first ?? other;
}

Itu cukup jelas, singkat, mencakup semua kasus, dan tidak perlu membuat iterasi tambahan dari urutan tersebut.

Menjadikan ini menjadi metode umum yang berhasil IEnumerable<T>dibiarkan sebagai latihan. :-)


Misalnya, Anda memiliki urutan nullable dan nilai yang diekstrak juga merupakan nullable. Dalam hal ini, urutannya bisa kosong atau setiap item dalam urutan bisa memiliki null dalam nilai yang diekstrak. Penggabungan, dalam hal ini, akan mengembalikan othersaat nullsebenarnya (mungkin) respons yang benar. Katakanlah fungsi itu T Unanimous<U, T>(this IEnumerable<U> sequence, T other)atau tanda tangan semacam itu, yang sedikit memperumitnya.
Anthony Pegram

@Anthony: Memang, ada banyak komplikasi di sini, tetapi komplikasi tersebut cukup mudah diatasi. Saya menggunakan int nullable untuk kenyamanan sehingga saya tidak perlu mendeklarasikan flag "Saya sudah melihat item pertama". Anda dapat dengan mudah mendeklarasikan benderanya. Juga saya menggunakan "int" daripada T karena saya tahu bahwa Anda selalu dapat membandingkan dua int untuk persamaan, yang tidak terjadi untuk dua Ts. Ini lebih merupakan sketsa solusi daripada solusi generik yang berfungsi penuh.
Eric Lippert

13
return collection.All(i => i == collection.First())) 
    ? collection.First() : otherValue;.

Atau jika Anda khawatir tentang mengeksekusi First () untuk setiap elemen (yang bisa menjadi masalah kinerja yang valid):

var first = collection.First();
return collection.All(i => i == first) ? first : otherValue;

@KeS - Itulah sebabnya saya menambahkan bagian kedua dari jawaban saya. Pada koleksi kecil, memanggil First () itu sepele. Pada koleksi besar, itu mungkin mulai menjadi masalah.
Justin Niessner

1
"Pada koleksi kecil, memanggil First () itu sepele." - Itu tergantung dari sumber koleksinya. Untuk daftar atau larik objek sederhana, Anda benar. Namun, beberapa enumerabel bukanlah kumpulan primitif yang disimpan dalam cache memori. Kumpulan delegasi, atau enumerator yang menghasilkan melalui perhitungan deret algoritmik (misalnya Fibonacci), akan menjadi sangat mahal untuk mengevaluasi First () setiap saat.
KeithS

5
Atau lebih buruk, jika kueri adalah kueri database dan memanggil "Pertama" akan memukul database lagi setiap saat.
Eric Lippert

1
Lebih parah lagi kalau iterasi satu kali seperti membaca dari file ... Jadi jawaban Ani dari utas lain terlihat paling bagus.
Alexei Levenkov

@ Eric - Ayo. Tidak ada yang salah dengan memukul database tiga kali untuk setiap elemen ... :-P
Justin Niessner

3

Ini mungkin terlambat, tetapi ekstensi yang berfungsi untuk jenis nilai dan referensi sama berdasarkan jawaban Eric:

public static partial class Extensions
{
    public static Nullable<T> Unanimous<T>(this IEnumerable<Nullable<T>> sequence, Nullable<T> other, IEqualityComparer comparer = null)  where T : struct, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (Nullable<T>)first ?? other;
    }

    public static T Unanimous<T>(this IEnumerable<T> sequence, T other, IEqualityComparer comparer = null)  where T : class, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (T)first ?? other;
    }
}

1
public int GetResult(List<int> list){
int first = list.First();
return list.All(x => x == first) ? first : SOME_OTHER_VALUE;
}

1

Alternatif untuk menggunakan LINQ:

var set = new HashSet<int>(values);
return (1 == set.Count) ? values.First() : otherValue;

Saya telah menemukan penggunaan HashSet<T>lebih cepat untuk daftar hingga ~ 6.000 bilangan bulat dibandingkan dengan:

var value1 = items.First();
return values.All(v => v == value1) ? value1: otherValue;

Pertama, hal ini dapat membuat banyak sampah. Juga kurang jelas dibandingkan jawaban LINQ lainnya, tetapi lebih lambat dari jawaban metode ekstensi.
Ian Ringrose

Benar. Namun, tidak akan banyak sampah jika kita berbicara tentang menentukan apakah sekumpulan kecil nilai semuanya sama. Ketika saya menjalankan ini dan pernyataan LINQ di LINQPad untuk sekumpulan kecil nilai, HashSet lebih cepat (waktunya menggunakan kelas Stopwatch).
Ɖiamond ǤeezeƦ

Jika Anda menjalankannya dalam rilis build dari baris perintah, Anda mungkin akan mendapatkan hasil yang berbeda.
Ian Ringrose

Membuat aplikasi konsol dan menemukan yang HashSet<T>awalnya lebih cepat daripada menggunakan pernyataan LINQ dalam jawaban saya. Namun, jika saya melakukan ini dalam satu lingkaran, maka LINQ lebih cepat.
Ɖiamond ǤeezeƦ

Masalah besar dengan solusi ini adalah jika Anda menggunakan kelas kustom Anda, Anda harus mengimplementasikan kelas Anda sendiri GetHashCode(), yang sulit dilakukan dengan benar. Lihat: stackoverflow.com/a/371348/2607840 untuk lebih jelasnya.
Cameron

0

Sedikit variasi dari pendekatan sederhana di atas.

var result = yyy.Distinct().Count() == yyy.Count();


3
Ini justru sebaliknya. Ini akan memeriksa bahwa setiap elemen dalam daftar adalah Unik.
Mario Galea

-1

Jika sebuah array berjenis multidimensi seperti di bawah ini maka kita harus menulis di bawah linq untuk memeriksa datanya.

contoh: di sini elemen adalah 0 dan saya memeriksa semua nilai adalah 0 atau tidak.
ip1 =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

    var value=ip1[0][0];  //got the first index value
    var equalValue = ip1.Any(x=>x.Any(xy=>xy.Equals()));  //check with all elements value 
    if(equalValue)//returns true or false  
    {  
    return "Same Numbers";  
    }else{  
    return "Different Numbers";   
    }
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.