Tüm liste öğelerinin aynı değere sahip olup olmadığı ve döndürüp döndürmediği veya yoksa "diğerDeğer" döndürülüp döndürülmediği nasıl kontrol edilir?


122

Listedeki tüm öğeler aynı değere sahipse, bu değeri kullanmam gerekir, aksi takdirde bir "otherValue" kullanmam gerekir. Bunu yapmanın basit ve net bir yolunu düşünemiyorum.

Ayrıca , bir koleksiyondaki ilk öğe için özel mantığa sahip olan düzgün yazma döngüsüne bakın .


Oldukça arsız dikkat çekicinize, Ani'nin cevabı stackoverflow.com/questions/4390232/…
Binary Worrier

5
Liste boş olduğu için ilk değer yoksa ne olmasını istersiniz? Bu durumda , "listedeki tüm öğelerin aynı değere sahip olduğu" doğrudur - bana inanmıyorsanız, bana olmayan bir tane bulun! Bu durumda ne yapacağınızı tanımlamazsınız. Bu bir istisna oluşturmalı mı, "diğer" değerini mi döndürmeli yoksa ne?
Eric Lippert

@Eric, liste boş olduğunda üzgünüm, "diğer" değerini döndürmeli
Ian Ringrose

Yanıtlar:


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

Aklıma gelen en temiz yol. Val'i satır içine alarak bunu tek satırlık bir hale getirebilirsiniz, ancak First () n kez değerlendirilerek yürütme süresini iki katına çıkarır.

Yorumlarda belirtilen "boş küme" davranışını dahil etmek için, yukarıdaki ikisinden önce bir satır daha eklemeniz yeterlidir:

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

1
+1, .Anyfarklı değerlerin olduğu durumlarda numaralandırmanın erken çıkmasına izin ver kullanılır mı?
Jeff Ogata

12
@adrift: dizinin Allbir öğesine ulaşır ulaşmaz sona erer . Benzer şekilde, dizinin bir öğesine çarptığı anda sona erer . Yani, hem ve sergi "kısa devre" analog için ve (neyi etkili biçimde olduğu ve vardır). xx.Value != valAny(x => x.Value != val)xx.Value != valAllAny&&||AllAny
jason

@Jason: kesinlikle. Tüm (koşul) etkili! Herhangi biri (! Koşul) ve herhangi birinin değerlendirmesi, yanıt öğrenilir öğrenilmez sona erecektir.
KeithS

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

101

Herkes için iyi hızlı test:

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

1
ClassYapılar ile çalışması gerekse de, bu herhangi biriyle çalışmaz. Yine de ilkellerin listesi için harika.
Andrew Backer

2
KeithS'in IMO çözümünden +1 çok daha temiz. collection.Distinct().Count() <= 1 Boş koleksiyonlara izin vermek istiyorsanız kullanmak isteyebilirsiniz.
3dGrabber

4
Dikkatli olun, .Distinct()her zaman beklendiği gibi çalışmaz - özellikle nesnelerle çalışırken, bu soruya bakın . Bu durumlarda, IEquatable arayüzünü uygulamanız gerekir.
Matt

16
Daha temiz, evet, ancak ortalama durumda daha az performanslı; Distinct () 'in koleksiyondaki her bir öğeyi bir kez geçmesi garanti edilir ve en kötü durumda, her öğenin farklı olması durumunda, Count () tam listeyi iki kez geçecektir. Distinct () ayrıca bir HashSet oluşturur, böylece davranışı doğrusal olabilir ve NlogN veya daha kötü olamaz ve bu bellek kullanımını şişirir. All (), tüm öğelerin eşit olduğu en kötü durumda bir tam geçiş yapar ve yeni koleksiyon oluşturmaz.
KeithS

1
@KeithS Umarım şimdiye kadar fark etmişsinizdir Distinct, koleksiyonu hiç Countgeçmeyecek ve Distinct'ın yineleyicisi aracılığıyla bir geçiş yapacak .
NetMage

22

Mevcut sıra operatörlerinden kesinlikle böyle bir cihaz oluşturabilseniz de, bu durumda bunu özel bir sıra operatörü olarak yazmaya meyilli olurum. Gibi bir şey:

// 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;
}

Bu oldukça açık, kısadır, tüm vakaları kapsar ve gereksiz yere sekansın fazladan yinelemelerini oluşturmaz.

Bunu, üzerinde çalışan genel bir yöntem haline getirmek IEnumerable<T>, bir egzersiz olarak bırakılmıştır. :-)


Örneğin, bir nullable dizisine sahip olduğunuzu ve ayıklanan değerin de bir null yapılabilir olduğunu varsayalım. Bu durumda, dizi boş olabilir veya dizideki her öğenin ayıklanan değerde bir boş değeri olabilir. Coalescing, bu durumda, dönecekti otherzaman nullaslında (muhtemelen) Doğru yanıt oldu. Diyelim ki fonksiyon T Unanimous<U, T>(this IEnumerable<U> sequence, T other)biraz karmaşıklaştırıyor.
Anthony Pegram

@Anthony: Gerçekten, burada pek çok zorluk var, ancak bunlar kolaylıkla çözülebilir. Kolaylık olarak boş değer atanabilir bir int kullanıyorum, böylece "İlk öğeyi zaten gördüm" bayrağı bildirmek zorunda kalmıyorum. Bayrağı kolayca ilan edebilirsin. Ayrıca T yerine "int" kullanıyorum çünkü her zaman eşitlik için iki girişi karşılaştırabileceğinizi biliyorum, ki bu iki T için geçerli değildir. Bu, tamamen işlevsel bir genel çözümden çok bir çözüm taslağıdır.
Eric Lippert

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

Veya her öğe için First () 'ü çalıştırmaktan endişeleniyorsanız (bu geçerli bir performans sorunu olabilir):

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

@KeithS - Cevabımın ikinci bölümünü bu yüzden ekledim. Küçük koleksiyonlarda First () çağırmak önemsizdir. Büyük koleksiyonlarda bu bir sorun olmaya başlayabilir.
Justin Niessner

1
"Küçük koleksiyonlarda First () 'ü aramak önemsizdir." - Bu, koleksiyonun kaynağına bağlı. Basit nesnelerin bir listesi veya dizisi için kesinlikle haklısınız. Ancak, bazı numaralandırılabilirler, önbelleğe alınmış sonlu ilkel kümeler değildir. Bir delege koleksiyonu veya algoritmik seri hesaplaması (örneğin Fibonacci) yoluyla sonuç veren bir numaralayıcı, First () 'ü her seferinde değerlendirmek çok pahalıya mal olacaktır.
KeithS

5
Veya daha da kötüsü, sorgu bir veritabanı sorgusuysa ve "İlk" çağrısı veritabanına her seferinde tekrar ulaşır.
Eric Lippert

1
Dosyadan okumak gibi bir defalık yinelemeniz olduğunda durum daha da kötüleşiyor ... Yani Ani'nin diğer ileti dizisindeki cevabı en iyi görünüyor.
Alexei Levenkov

@Eric - Hadi. Veritabanına her öğe için üç kez vurmanın yanlış bir
tarafı yok

3

Bu geç olabilir, ancak Eric'in cevabına göre hem değer hem de referans türleri için çalışan bir uzantı:

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

LINQ kullanmaya bir alternatif:

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

HashSet<T>Aşağıdakilere kıyasla ~ 6.000'e kadar tam sayı içeren listeler için kullanmanın daha hızlı olduğunu buldum :

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

Öncelikle bu çok fazla çöp oluşturabilir. Ayrıca, diğer LINQ yanıtlarından daha az nettir, ancak uzantı yöntemi yanıt verdiklerinde daha yavaştır.
Ian Ringrose

Doğru. Bununla birlikte, küçük bir değerler kümesinin hepsinin aynı olup olmadığını belirlemekten bahsediyorsak, o kadar da gereksiz olmayacaktır. Bunu ve LINQPad'de bir LINQ ifadesini küçük bir değerler kümesi için çalıştırdığımda, HashSet daha hızlıydı (Kronometre sınıfı kullanılarak zamanlandı).
Ɖiamond ǤeezeƦ

Komut satırından bir sürüm yapısında çalıştırırsanız, farklı sonuçlar elde edebilirsiniz.
Ian Ringrose

Bir konsol uygulaması oluşturdum ve HashSet<T>başlangıçta cevabımda LINQ ifadelerini kullanmaktan daha hızlı buldum. Ancak, bunu bir döngüde yaparsam, LINQ daha hızlı olur.
Ɖiamond ǤeezeƦ

Bu çözümle ilgili en büyük sorun, özel sınıflarınızı kullanıyorsanız kendi sınıfınızı uygulamanız GetHashCode()gerektiğidir ve bunu doğru bir şekilde yapmak zordur Daha fazla ayrıntı için bkz. Stackoverflow.com/a/371348/2607840 .
Cameron

0

Yukarıdaki basitleştirilmiş yaklaşımda küçük bir değişiklik.

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


3
Bu tam tersi. Bu, listedeki her öğenin Benzersiz olup olmadığını kontrol edecektir.
Mario Galea

-1

Bir dizi aşağıdaki gibi çok boyutlu tipte ise, veriyi kontrol etmek için linq'in altına yazmamız gerekir.

örnek: burada elemanlar 0 ve tüm değerlerin 0 olup olmadığını kontrol ediyorum.
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";   
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.