2010-02-11 6 views
14

Aşağıdaki kodu kullanarakLinq's TakeWhile'den 1 tane daha öğe nasıl alabilirim?

(ilgilenilen kod satırı geri kalanı tam temsil için sadece olduğunu, sonuncusu), ben gerekli maksimum oy aştı kadar seçmenleri almak istedim, ama doğru durur Bu maksimum oy sayısına ulaşmadan önce, seçmenler havuzunun istediğimden 1 tane daha az oyu var.

LINQ'da temiz bir yol var mı? Oy verebilseydim, oysa maksimum sayıya ulaştı mı? Bir seçmen daha ekleyebilirim ya da bunu bir döngüde yapabilirim ama bunun yerine LINQ ile bunu yapmanın iyi bir yolu olup olmadığını merak ediyorum.

var voters = new List<Person> 
          { 
           new Person("Alice", Vote.Yes), 
           new Person("Bob", Vote.Yes), 
           new Person("Catherine", Vote.No), 
           new Person("Denzel", Vote.Yes), 
           new Person("Einrich", Vote.Abstain), 
           new Person("Frederica", Vote.Abstain), 
           new Person("Goeffried", Vote.Abstain), 
          }; 
      voters.Single(c => c.Name == "Alice").Voices = 100; 
      voters.Single(c => c.Name == "Bob").Voices = 150; 
      voters.Single(c => c.Name == "Catherine").Voices = 99; 
      voters.Single(c => c.Name == "Denzel").Voices = 24; 
      voters.Single(c => c.Name == "Einrich").Voices = 52; 
      voters.Single(c => c.Name == "Frederica").Voices = 39; 
      voters.Single(c => c.Name == "Goeffried").Voices = 99; 

// this takes voters until we are BEFORE reaching X voices... 
int voicesSoFar = 0; 
int voicesNeeded = 300; 
var eligibleVoters = voters.TakeWhile((p => (voicesSoFar += p.Voices) < voicesNeeded)); 

cevap

15

Sen

aradığınız
voters.TakeWhile(p => { 
    bool exceeded = voicesSoFar > voicesNeeded ; 
    voicesSoFar += p.Voices; 
    return !exceeded; 
}); 

bir kereye astar ısrar ederse, bu, daha önceki değeri karşılaştırarak çalışır: benim yaptığım bir son durumunu çarpana kadar ve dahil ben bir işlevi yürütmek için istediği yere bir durumda

voters.TakeWhile(p => (voicesSoFar += p.Voices) - p.Voices < voicesNeeded); 
+0

Not: Unutmayın 'seslerSoFar', döngüdeki sonu doğru değil, bir yardımcı değişken atıyor. – Kobi

+0

+1 Gereksiz bir uzantı yöntemi yazmak gerektirmeyen bir çözüm için. –

+0

Garip ama hiçbir şey göstermek için ilk sürümü alamıyorum ... Tek liner mükemmel çalışıyor olsa da. –

6

Sadece kendi uzantısı yöntem yazmak:

static class IEnumerableExtensions { 
    public static IEnumerable<T> TakeUntil<T>(
     this IEnumerable<T> elements, 
     Func<T, bool> predicate 
    ) { 
     return elements.Select((x, i) => new { Item = x, Index = i }) 
         .TakeUntil((x, i) => predicate(x.Item)) 
         .Select(x => x.Item); 
    } 

    public static IEnumerable<T> TakeUntil<T>(
     this IEnumerable<T> elements, 
     Func<T, int, bool> predicate 
    ) { 
     int i = 0; 
     foreach (T element in elements) { 
      if (predicate(element, i)) { 
       yield return element; 
       yield break; 
      } 
      yield return element; 
      i++; 
     } 
    } 
} 

Kullanımı:

var eligibleVoters = voters.TakeUntil(
         p => (voicesSoFar += p.Voices) >= voicesNeeded 
        ); 

foreach(var voter in eligibleVoters) { 
    Console.WriteLine(voter.Name); 
} 

Çıktı:

Alice 
Bob 
Catherine 
+1

Yani, bir dış değişkeni mutasyona uğratan lambda ifadeniz beni çok rahatsız ediyor. Özellikle, 'elverişliVoterleri' iki kez saymak ve sadece kötü olan aynı sonuçları göremezsiniz. – jason

+0

Evet, daha sonra bu yeni soruyu şu şekilde başlattım: http://stackoverflow.com/questions/2242371/does-this-code-really-cause-an-access-to-modified-closure-problem As Şimdilik, başımı bu kodun etrafına sarmaya çalışıyorum, bunun için yeniyim: P –

+1

@PRINCESS FLUFF: Önce ikinci yönteme odaklanın; ilki sadece ikinciyi süslü bir şekilde çağırır. Temel olarak 'TakeWhile' indeksli taban ve diğeri olmayan iki aşırı yüklenme olduğu gerçeğini taklit ettim. – jason

19

:

public static IEnumerable<T> TakeUntilIncluding<T>(this IEnumerable<T> list, Func<T, bool> predicate) 
{ 
    foreach(T el in list) 
    { 
     yield return el; 
     if (predicate(el)) 
      yield break; 
    } 
} 

Benim için çalıştı! Bence bu Jason'lar gibi bir uygulama-teşhis çözümü, ama daha basit.

+1

Ve harici/yakalanmış durum değişkeni yok. – Tormod

0

Kobi'nin cevabının değiştirilmesi ancak (value, index) kullanımını gösterir. index, OP'leri değil de benzer sorunları çözmek için kullanışlıdır.

voters.TakeWhile((value, index) => (voicesSoFar += value.Voices) - value.Voices < voicesNeeded); 
0

Aynı sorunla karşılaşıyordum. o da ilk sonuçların bazı int alır yöntemi vardır al edilir

IEnumerable<Something> newSomethings = somethings.SkipWhile(s => s != stop).Skip(1); 

kadar

IEnumerable<Something> newSomethings = somethings.TakeWhile(s => s != stop).Union(new List<Something>(){stop}); 

ve atlama için

yaşına kadar Öyle almaya, Birlik kullanılan ve yöntemleri atla gelmiş.

İlgili konular