2010-09-27 22 views
22

IEnumerable'un tam olarak tek bir öğe içerdiğini kontrol etmek istiyorum. Count() tüm listeyi numaralandırma olarakBir sekansın belirli bir uzunluğa sahip olmasını nasıl sağlayabilirim?

bool hasOneElement = seq.Count() == 1 

Ancak, çok verimli değil: Bu parçacık eser yok. Açıkçası, bir listenin bittiğini bilmek veya 1'den fazla eleman içermek boş değil demektir. Bu kısa devre davranışına sahip bir uzatma yöntemi var mı?

+0

Kodumda yaptığım mantık hatası, en azından * ve * tam olarak * ile ilgili olarak insanları atmış olabilir. Bence şu an en iyi çözüme ulaştık. – guhou

+1

Sayımı(), geçirilen IEnumerable 'un ICollection 'a verilmediğini belirlerse, yalnızca listenin tamamını yineler. Yani bir List örneğini veya bir diziyi iletirseniz, yinelenmez. –

+1

Bence bu tamamen erken bir optimizasyon. –

cevap

20

Bu yapmalıyım

public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source) 
{ 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     return iterator.MoveNext() && !iterator.MoveNext(); 
    } 
} 

Bu tür olduğunu funky olan hile, ama muhtemelen üretim kodunda kullanılmamalıdır. Sadece yeterince açık değil. Geldiğin ben sadece gördüm:)

DÜZENLEME; eğlenceli ederken & & operatörün LHS'deki yan etki RHS uygun çalışması için gerekli olması ... sadece kötü tam olarak aynı şey ile ama keyfi bir uzunluk için. Son dönüş ifadeniz yanlış olsa da - !en.MoveNext() olmalıdır.

public static bool CountEquals<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException("source"); 
    } 
    if (count < 0) 
    { 
     throw new ArgumentOutOfRangeException("count", 
               "count must not be negative"); 
    } 
    // We don't rely on the optimizations in LINQ to Objects here, as 
    // they have changed between versions. 
    ICollection<T> genericCollection = source as ICollection<T>; 
    if (genericCollection != null) 
    { 
     return genericCollection.Count == count; 
    } 
    ICollection nonGenericCollection = source as ICollection; 
    if (nonGenericCollection != null) 
    { 
     return nonGenericCollection.Count == count; 
    } 
    // Okay, we're finally ready to do the actual work... 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      if (!iterator.MoveNext()) 
      { 
       return false; 
      } 
     } 
     // Check we've got no more 
     return !iterator.MoveNext(); 
    } 
} 

DÜZENLEME: Ve CountEquals bir özyinelemeli formu (bu kullanmayın lütfen, fonksiyonel hayranları için şimdi İşte tam ICollection/ICollection<T> için güzel bir isim (IMO), argüman denetimi ve optimizasyonu ile yöntemidir bu) espri olsun diye sadece burada:

public static bool CountEquals<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException("source"); 
    } 
    if (count < 0) 
    { 
     throw new ArgumentOutOfRangeException("count", 
               "count must not be negative"); 
    } 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     return IteratorCountEquals(iterator, count); 
    } 
} 

private static bool IteratorCountEquals<T>(IEnumerator<T> iterator, int count) 
{ 
    return count == 0 ? !iterator.MoveNext() 
     : iterator.MoveNext() && IteratorCountEquals(iterator, count - 1); 
} 

DÜZENLEME: LINQ to SQL gibi bir şey için olduğunu unutmayın, basit Count() yaklaşım kullanmalıdır - o veritabanına yerine getirilirken sonra yapılması gereken izin vereceğiz çünkü fiili sonuç. Listenin sıfır veya bir öğeye sahip olup olmadığını

+1

Evet çirkin ve evet, güzel :-) Ama hayır, bu kodu ilk gördüğünüzde çok açık değil. –

+3

LOL @Jon 'return iterator.MoveNext() &&! Iterator.MoveNext(); '. Cevap benden "ikisi" dir! – mauris

+2

+1 iterator.MoveNext() &&! Iterator.MoveNext() vay! – onof

7

Hayır, ama tek kendin yazabilirsiniz:

public static bool HasExactly<T>(this IEnumerable<T> source, int count) 
{ 
    if(source == null) 
     throw new ArgumentNullException("source"); 

    if(count < 0) 
     return false; 

    return source.Take(count + 1).Count() == count; 
} 

DÜZENLEME: açıklama sonra tam için en az değiştirilmiştir. Sen hangi yapmanızı sağlayan, Cevabıma here bir göz atmak isteyebilirsiniz (sekans ICollection veya ICollection<T> bu durumda numaralandırma gerekli değildir uygulaması halinde sadece 1 numaralandırıcıyı ve kontroller kullanır) daha genel ve etkili çözüm için

Exact, AtLeast veya AtMost testlerini aradığınızı belirtin.

+0

Hala bu snippet'teki tüm listeyi numaralandırıyor musunuz? ("Al" ve "Say" dan) – guhou

+0

'dan dolayı * en azından * diye sormuyorum, * tam olarak *. – guhou

+0

@ BleuM937: Düzenlendi. – Ani

0

Aradığınız şeyin .Single() olduğuna inanıyorum. Tam olarak bir başkası, yakalayabileceğiniz InvalidOperationException öğesini atar.

public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source) 
{ 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     // Check we've got at least one item 
     if (!iterator.MoveNext()) 
     { 
      return false; 
     } 
     // Check we've got no more 
     return !iterator.MoveNext(); 
    } 
} 

Sen ayrıca bu elide olabilir, ama bunu önermiyoruz:

http://msdn.microsoft.com/nb-no/library/bb155325.aspx

+1

Sanırım yapmaya çalıştığım şey, bununla elde edilebilirdi, ancak kontrol akışı için istisnalar atmayı sevmiyorum. – guhou

+1

Ardından SingleOrDefault() öğesini kullanın ve dönüş değeri sıfır olup olmadığını kontrol edin. – danijels

+0

SingleOrDefault listede birden fazla öğe varsa yine de bir istisna atar. Bunun gidiş yolu olduğunu sanmıyorum, sayım yapan kod daha verimli olmalı. – SamStephens

4

seq.Skip(1).Any() söyleyecektir.

Yaptığınız düzenlemenin uzunluğu kontrol etmenin en etkili yolu olduğunu düşünüyorum. Fakat bir mantık hatası var, uzunluktan daha az olan öğeler doğru olarak geri dönecek. İkinci beyannameye ne yaptığımı görün.

public static bool LengthEquals<T>(this IEnumerable<T> en, int length) 
    { 
     using (var er = en.GetEnumerator()) 
     { 
      for (int i = 0; i < length; i++) 
      { 
       if (!er.MoveNext()) 
        return false; 
      } 
      return !er.MoveNext(); 
     } 
    } 
+0

Evet, sadece kendimi fark ediyordum. Ben sizin yöntem isminizi tercih ediyorum, ama benim değişken ismim :) - Aslında, CountEquals'ı tercih ettiğimden eminim, çünkü Count() yöntemi ile daha iyi uyuyor :) –

+0

Evet, CountEquals da bana da geliyor. – SamStephens

1

Bu nasıl?

public static bool CountEquals<T>(this IEnumerable<T> source, int count) { 
    return source.Take(count + 1).Count() == count; 
} 

Take() biz MoveNext daha count+1 den kez aramak asla emin olacaktır. Count() sadece Count üyesi bakmak için optimize edilmiştir, çünkü

Ben ICollection herhangi örneği için dikkat etmek istiyorum

, orijinal uygulama source.Count() == count daha hızlı olmalıdır.

İlgili konular